{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using TensorFlow backend.\n"
     ]
    }
   ],
   "source": [
    "from nltk.corpus import movie_reviews as reviews\n",
    "from sklearn.datasets import fetch_20newsgroups\n",
    "from gensim.models import KeyedVectors\n",
    "from gensim.models import word2vec\n",
    "\n",
    "import wget\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import re\n",
    "import datetime\n",
    "from operator import itemgetter\n",
    "from random import randint\n",
    "import seaborn as sns\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "import os\n",
    "import time\n",
    "import string\n",
    "import dill\n",
    "import pickle\n",
    "import gzip\n",
    "\n",
    "from nltk import *\n",
    "from nltk import wordpunct_tokenize, WordNetLemmatizer, sent_tokenize, pos_tag\n",
    "from nltk.corpus import stopwords as sw, wordnet as wn\n",
    "from nltk.stem.snowball import SnowballStemmer\n",
    "\n",
    "from sklearn.base import BaseEstimator, TransformerMixin\n",
    "from sklearn.pipeline import Pipeline, FeatureUnion, make_pipeline\n",
    "from sklearn.preprocessing import LabelEncoder, FunctionTransformer\n",
    "from sklearn.linear_model import SGDClassifier\n",
    "from sklearn.svm import SVC\n",
    "from sklearn.naive_bayes import MultinomialNB\n",
    "from sklearn.base import BaseEstimator, TransformerMixin\n",
    "from sklearn.metrics import precision_score, accuracy_score, confusion_matrix, classification_report as clsr\n",
    "from sklearn.feature_extraction.text import TfidfVectorizer, TfidfTransformer, CountVectorizer\n",
    "from sklearn.model_selection import GridSearchCV, train_test_split as tts\n",
    "from sklearn.manifold import TSNE\n",
    "from sklearn.multiclass import OneVsRestClassifier\n",
    "\n",
    "import tensorflow as tf\n",
    "\n",
    "from keras.preprocessing.text import Tokenizer\n",
    "from keras.preprocessing.sequence import pad_sequences\n",
    "from keras.models import Sequential, Model, model_from_json\n",
    "from keras.layers.normalization import BatchNormalization\n",
    "from keras.layers.embeddings import Embedding\n",
    "from keras.layers import Dense, LSTM, SpatialDropout1D, Activation, Conv1D, MaxPooling1D, Input, concatenate\n",
    "from keras.utils.np_utils import to_categorical"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Data retrieving"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load from .csv file with complete dataset\n",
    "data_essays = pd.read_csv('./Data/essays.csv', encoding = \"ISO-8859-1\")\n",
    "data_essays['cEXT'] = np.where(data_essays['cEXT']=='y', 1, 0)\n",
    "data_essays['cNEU'] = np.where(data_essays['cNEU']=='y', 1, 0)\n",
    "data_essays['cAGR'] = np.where(data_essays['cAGR']=='y', 1, 0)\n",
    "data_essays['cCON'] = np.where(data_essays['cCON']=='y', 1, 0)\n",
    "data_essays['cOPN'] = np.where(data_essays['cOPN']=='y', 1, 0)\n",
    "X_essays = data_essays['TEXT'].tolist()\n",
    "y_essays = data_essays[['cEXT', 'cNEU', 'cAGR', 'cCON', 'cOPN']]\n",
    "data_essays['text length'] = data_essays['TEXT'].apply(len)\n",
    "labels = ['cEXT', 'cNEU', 'cAGR', 'cCON', 'cOPN']\n",
    "X_train, X_test, y_train, y_test = tts(X_essays, y_essays, test_size=0.2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Train-test split to save the dataset\n",
    "with open('./Data/X_train.pkl', 'wb') as f:\n",
    "    pickle.dump(X_train, f)\n",
    "with open('./Data/X_test.pkl', 'wb') as f:\n",
    "    pickle.dump(X_test, f)\n",
    "with open('./Data/y_train.pkl', 'wb') as f:\n",
    "    pickle.dump(y_train, f)\n",
    "with open('./Data/y_test.pkl', 'wb') as f:\n",
    "    pickle.dump(y_test, f)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load train and test sets from pickled lists\n",
    "with open('./Data/X_train.pkl', 'rb') as pickle_file:\n",
    "    X_train = pickle.load(pickle_file)\n",
    "with open('./Data/X_test.pkl', 'rb') as pickle_file:\n",
    "    X_test = pickle.load(pickle_file)\n",
    "with open('./Data/y_train.pkl', 'rb') as pickle_file:\n",
    "    y_train = pickle.load(pickle_file)\n",
    "with open('./Data/y_test.pkl', 'rb') as pickle_file:\n",
    "    y_test = pickle.load(pickle_file)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "heading_collapsed": true
   },
   "source": [
    "### Visualization"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "metadata": {
    "hidden": true
   },
   "outputs": [],
   "source": [
    "import random\n",
    "class visualize:\n",
    "\n",
    "    def __init__(self, complete_dataset, X, labels_list):\n",
    "        self.data = complete_dataset\n",
    "        self.X = X\n",
    "        self.labels_list = labels_list\n",
    "\n",
    "    def textlength_vs_labels_histogram(self):\n",
    "        # Visualization of histograms of text length vs. label\n",
    "        for label in self.labels_list:\n",
    "            g = sns.FacetGrid(data=self.data, col=label)\n",
    "            g.map(plt.hist, 'text length', bins=50)\n",
    "        plt.show()\n",
    "\n",
    "    def textlength_vs_labels_boxplot(self):\n",
    "        # Visualization of boxplots of text length vs. label\n",
    "        for i, label in enumerate(self.labels_list):\n",
    "            plt.figure(i)\n",
    "            sns.boxplot(x=label, y='text length', data=self.data)\n",
    "        plt.show()\n",
    "\n",
    "    def most_frequent_words(self):\n",
    "        # Visualization of the most frequent words\n",
    "        complete_corpus = ' '.join(self.X)\n",
    "        words = tokenize.word_tokenize(complete_corpus)\n",
    "        fdist = FreqDist(words)\n",
    "        print(\"List of 100 most frequent words/counts\")\n",
    "        print(fdist.most_common(100))\n",
    "        fdist.plot(40)\n",
    "\n",
    "    def most_frequent_words_preprocessed(self):\n",
    "        # Visualization of the most frequent words\n",
    "        if not hasattr(self, 'X_preprocess'):\n",
    "            preprocessor = train(corpus = self.X).NLTKPreprocessor\n",
    "            self.X_preprocess = prep.transform(self.X).tolist()\n",
    "        complete_corpus = ' '.join(self.X_preprocess)\n",
    "        words = tokenize.word_tokenize(complete_corpus)\n",
    "        fdist = FreqDist(words)\n",
    "        print(\"List of 100 most frequent words/counts\")\n",
    "        print(fdist.most_common(100))\n",
    "        fdist.plot(40)\n",
    "\n",
    "    def get_corpus_statistics(self):\n",
    "        # Retrieve some info on the text data\n",
    "        numWords = []\n",
    "        for text in self.X:\n",
    "                counter = len(text.split())\n",
    "                numWords.append(counter)  \n",
    "        numFiles = len(numWords)\n",
    "        print('The total number of essays is', numFiles)\n",
    "        print('The total number of words in all essays is', sum(numWords))\n",
    "        print('The average number of words in each essay is', sum(numWords)/len(numWords))\n",
    "\n",
    "    def get_preprocessed_corpus_statistics(self):\n",
    "        # Retrieve some info on the preprocessed text data\n",
    "        if not hasattr(self, 'X_preprocess'):\n",
    "            preprocessor = train(corpus = self.X).NLTKPreprocessor\n",
    "            self.X_preprocess = prep.transform(self.X).tolist()\n",
    "        len_list = [np.count_nonzero(self.X_preprocess[i]) for i in range(len(self.X))]\n",
    "        print('The average number of words in each preprocessed essay is', np.mean(len_list))\n",
    "        print('The standard deviation of the number of words in each preprocessed essay is', np.std(len_list))\n",
    "        print('The average number of words in each preprocessed essay plus 2 standard deviations is', np.mean(len_list) + 2 * np.std(len_list))\n",
    "\n",
    "class tsne:\n",
    "    \n",
    "    def __init__(self, X, max_features = 30000, max_sentence_len = 300, embed_dim = 300,  n_elements = 100):\n",
    "        self.X = X\n",
    "        self.max_features =max_features\n",
    "        self.max_sentence_len = max_sentence_len\n",
    "        self.embed_dim = embed_dim\n",
    "        self.n_elements = n_elements\n",
    "        self.vectors, self.words, self.dic =  self.prepare_embedding(self.X)\n",
    "\n",
    "    def load_google_vec(self):\n",
    "        url = 'https://s3.amazonaws.com/dl4j-distribution/GoogleNews-vectors-negative300.bin.gz'\n",
    "        #wget.download(url, 'Data/GoogleNews-vectors.bin.gz')\n",
    "        return KeyedVectors.load_word2vec_format(\n",
    "            'Data/GoogleNews-vectors.bin.gz',\n",
    "            binary=True)\n",
    "\n",
    "    def lemmatize_token(self, token, tag):\n",
    "        tag = {\n",
    "            'N': wn.NOUN,\n",
    "            'V': wn.VERB,\n",
    "            'R': wn.ADV,\n",
    "            'J': wn.ADJ\n",
    "        }.get(tag[0], wn.NOUN)\n",
    "        return WordNetLemmatizer().lemmatize(token, tag)\n",
    "\n",
    "\n",
    "    def get_preprocessed_corpus(self, X_corpus):\n",
    "        \"\"\"\n",
    "        Returns a preprocessed version of a full corpus (ie. tokenization and lemmatization using POS taggs)\n",
    "        \"\"\"\n",
    "        X = ' '.join(X_corpus)\n",
    "        lemmatized_tokens = []\n",
    "\n",
    "        # Break the document into sentences\n",
    "        for sent in sent_tokenize(X):\n",
    "\n",
    "            # Break the sentence into part of speech tagged tokens\n",
    "            for token, tag in pos_tag(wordpunct_tokenize(sent)):\n",
    "\n",
    "                # Apply preprocessing to the token\n",
    "                token = token.lower()\n",
    "                token = token.strip()\n",
    "                token = token.strip('_')\n",
    "                token = token.strip('*')\n",
    "\n",
    "                # If punctuation or stopword, ignore token and continue\n",
    "                if token in set(sw.words('english')) or all(char in set(string.punctuation) for char in token):\n",
    "                    continue\n",
    "\n",
    "                # Lemmatize the token and yield\n",
    "                lemma = self.lemmatize_token(token, tag)\n",
    "                lemmatized_tokens.append(lemma)\n",
    "\n",
    "        doc = ' '.join(lemmatized_tokens)\n",
    "        return doc\n",
    "\n",
    "\n",
    "    def prepare_embedding(self, X):\n",
    "        \"\"\"\n",
    "        Returns the embedding weights matrix, the word index, and the word-vector dictionnary corresponding\n",
    "        to the training corpus set of words.\n",
    "        \"\"\"\n",
    "        # Load Word2Vec vectors\n",
    "        word2vec = self.load_google_vec()\n",
    "\n",
    "        # Fit and apply an NLTK tokenizer on the preprocessed training corpus to obtain sequences.\n",
    "        tokenizer = Tokenizer(num_words=self.max_features)\n",
    "        X_pad = self.get_preprocessed_corpus(X)\n",
    "        tokenizer.fit_on_texts(pd.Series(X_pad))\n",
    "        X_pad = tokenizer.texts_to_sequences(pd.Series(X_pad))\n",
    "\n",
    "        # Pad the sequences\n",
    "        X_pad = pad_sequences(X_pad, maxlen=self.max_sentence_len, padding='post', truncating='post')\n",
    "\n",
    "        # Retrieve the word index\n",
    "        train_word_index = tokenizer.word_index\n",
    "\n",
    "        # Construct the embedding weights matrix and word-vector dictionnary\n",
    "        train_embedding_weights = np.zeros((len(train_word_index) + 1, self.embed_dim))\n",
    "        for word, index in train_word_index.items():\n",
    "            train_embedding_weights[index, :] = word2vec[word] if word in word2vec else np.random.rand(self.embed_dim)\n",
    "        word_vector_dict = dict(zip(pd.Series(list(train_word_index.keys())),\n",
    "                                    pd.Series(list(train_word_index.keys())).apply(\n",
    "                                        lambda x: train_embedding_weights[train_word_index[x]])))\n",
    "        return train_embedding_weights, train_word_index, word_vector_dict\n",
    "\n",
    "\n",
    "    def plot(self):\n",
    "        labels = []\n",
    "        tokens = []\n",
    "\n",
    "        l_bound = 0\n",
    "        u_bound = len(self.words)\n",
    "        step = int(len(self.words)/self.n_elements)\n",
    "\n",
    "        #for index in range(l_bound,u_bound, step):\n",
    "        for index in random.sample(range(l_bound,u_bound), self.n_elements):\n",
    "            tokens.append(self.vectors[index])\n",
    "            labels.append(self.words[index])\n",
    "\n",
    "        tsne_model = TSNE(perplexity=40, n_components=2, init='pca', n_iter=2500, random_state=23)\n",
    "        new_values = tsne_model.fit_transform(tokens)\n",
    "\n",
    "        xx = []\n",
    "        yy = []\n",
    "        for value in new_values:\n",
    "            xx.append(value[0])\n",
    "            yy.append(value[1])\n",
    "\n",
    "        plt.figure(figsize=(16, 16))\n",
    "        for i in range(len(xx)):\n",
    "            plt.scatter(xx[i],yy[i])\n",
    "            plt.annotate(labels[i],\n",
    "                         xy=(xx[i], yy[i]),\n",
    "                         xytext=(5, 2),\n",
    "                         textcoords='offset points',\n",
    "                         ha='right',\n",
    "                         va='bottom')\n",
    "        plt.show()\n",
    "\n",
    "\n",
    "class NLTKPreprocessor(BaseEstimator, TransformerMixin):\n",
    "    \"\"\"\n",
    "    Transforms input data by using NLTK tokenization, POS tagging, lemmatization and vectorization.\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self, corpus, max_sentence_len = 300, stopwords=None, punct=None, lower=True, strip=True):\n",
    "        \"\"\"\n",
    "        Instantiates the preprocessor.\n",
    "        \"\"\"\n",
    "        self.lower = lower\n",
    "        self.strip = strip\n",
    "        self.stopwords = set(stopwords) if stopwords else set(sw.words('english'))\n",
    "        self.punct = set(punct) if punct else set(string.punctuation)\n",
    "        self.lemmatizer = WordNetLemmatizer()\n",
    "        self.corpus = corpus\n",
    "        self.max_sentence_len = max_sentence_len\n",
    "\n",
    "    def fit(self, X, y=None):\n",
    "        \"\"\"\n",
    "        Fit simply returns self.\n",
    "        \"\"\"\n",
    "        return self\n",
    "\n",
    "    def inverse_transform(self, X):\n",
    "        \"\"\"\n",
    "        No inverse transformation.\n",
    "        \"\"\"\n",
    "        return X\n",
    "\n",
    "    def transform(self, X):\n",
    "        \"\"\"\n",
    "        Actually runs the preprocessing on each document.\n",
    "        \"\"\"\n",
    "        output = np.array([(self.tokenize(doc)) for doc in X])\n",
    "        return output\n",
    "\n",
    "    def tokenize(self, document):\n",
    "        \"\"\"\n",
    "        Returns a normalized, lemmatized list of tokens from a document by\n",
    "        applying segmentation, tokenization, and part of speech tagging.\n",
    "        Uses the part of speech tags to look up the lemma in WordNet, and returns the lowercase\n",
    "        version of all the words, removing stopwords and punctuation.\n",
    "        \"\"\"\n",
    "        lemmatized_tokens = []\n",
    "\n",
    "        # Clean the text\n",
    "        document = re.sub(r\"[^A-Za-z0-9^,!.\\/'+-=]\", \" \", document)\n",
    "        document = re.sub(r\"what's\", \"what is \", document)\n",
    "        document = re.sub(r\"\\'s\", \" \", document)\n",
    "        document = re.sub(r\"\\'ve\", \" have \", document)\n",
    "        document = re.sub(r\"can't\", \"cannot \", document)\n",
    "        document = re.sub(r\"n't\", \" not \", document)\n",
    "        document = re.sub(r\"i'm\", \"i am \", document)\n",
    "        document = re.sub(r\"\\'re\", \" are \", document)\n",
    "        document = re.sub(r\"\\'d\", \" would \", document)\n",
    "        document = re.sub(r\"\\'ll\", \" will \", document)\n",
    "        document = re.sub(r\"(\\d+)(k)\", r\"\\g<1>000\", document)\n",
    "\n",
    "        # Break the document into sentences\n",
    "        for sent in sent_tokenize(document):\n",
    "\n",
    "            # Break the sentence into part of speech tagged tokens\n",
    "            for token, tag in pos_tag(wordpunct_tokenize(sent)):\n",
    "\n",
    "                # Apply preprocessing to the token\n",
    "                token = token.lower() if self.lower else token\n",
    "                token = token.strip() if self.strip else token\n",
    "                token = token.strip('_') if self.strip else token\n",
    "                token = token.strip('*') if self.strip else token\n",
    "\n",
    "                # If punctuation or stopword, ignore token and continue\n",
    "                if token in self.stopwords or all(char in self.punct for char in token):\n",
    "                    continue\n",
    "\n",
    "                # Lemmatize the token\n",
    "                lemma = self.lemmatize(token, tag)\n",
    "                lemmatized_tokens.append(lemma)\n",
    "\n",
    "        doc = ' '.join(lemmatized_tokens)\n",
    "        tokenized_document = self.vectorize(np.array(doc)[np.newaxis])\n",
    "        return tokenized_document\n",
    "\n",
    "\n",
    "    def vectorize(self, doc):\n",
    "        \"\"\"\n",
    "        Returns a vectorized padded version of sequences.\n",
    "        \"\"\"\n",
    "        save_path = \"Data/padding.pickle\"\n",
    "        with open(save_path, 'rb') as f:\n",
    "            tokenizer = pickle.load(f)\n",
    "        doc_pad = tokenizer.texts_to_sequences(doc)\n",
    "        doc_pad = pad_sequences(doc_pad, padding='pre', truncating='pre', maxlen=self.max_sentence_len)\n",
    "        return np.squeeze(doc_pad)\n",
    "\n",
    "    def lemmatize(self, token, tag):\n",
    "        \"\"\"\n",
    "        Converts the Penn Treebank tag to a WordNet POS tag, then uses that\n",
    "        tag to perform WordNet lemmatization.\n",
    "        \"\"\"\n",
    "        tag = {\n",
    "            'N': wn.NOUN,\n",
    "            'V': wn.VERB,\n",
    "            'R': wn.ADV,\n",
    "            'J': wn.ADJ\n",
    "        }.get(tag[0], wn.NOUN)\n",
    "\n",
    "        return self.lemmatizer.lemmatize(token, tag)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "hidden": true
   },
   "source": [
    "##### Histograms of text length distrbution for the different labels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "metadata": {
    "hidden": true,
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x216 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x216 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x216 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x216 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x216 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "viz = visualize(data_essays, X_essays, labels)\n",
    "viz.textlength_vs_labels_histogram()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "hidden": true
   },
   "source": [
    "##### Boxplots of text length distrbution for the different labels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "metadata": {
    "hidden": true
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "viz.textlength_vs_labels_boxplot()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "hidden": true
   },
   "source": [
    "##### Most frequent words in the corpus"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "metadata": {
    "hidden": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "List of 100 most frequent words/counts\n",
      "[('I', 115468), ('.', 111178), ('to', 56263), (',', 47355), ('the', 38232), ('and', 36810), ('that', 29456), ('a', 28408), ('my', 26580), ('is', 25576), ('of', 22939), ('it', 22781), (\"n't\", 19996), ('in', 17828), ('do', 17448), ('have', 16166), ('me', 14588), ('so', 13099), ('but', 13060), ('this', 12054), ('be', 11724), ('for', 11520), (\"'s\", 11198), ('was', 10392), ('am', 10378), ('like', 10308), ('just', 10250), ('really', 10207), ('not', 10015), (\"'m\", 9973), ('on', 9015), ('about', 8941), ('with', 8708), ('think', 8061), ('are', 7602), ('what', 7517), ('all', 7475), ('at', 7469), ('because', 7144), ('i', 7047), ('know', 6959), ('get', 6875), ('he', 6605), ('now', 6154), ('would', 6077), ('you', 6013), ('if', 6001), ('time', 5966), ('out', 5923), ('they', 5905), ('up', 5743), ('or', 5733), ('going', 5621), ('go', 5576), ('she', 5556), ('?', 5539), ('want', 5483), ('will', 5420), ('can', 5276), ('!', 4959), ('as', 4939), ('people', 4898), ('her', 4879), ('when', 4867), ('we', 4836), ('much', 4702), ('It', 4626), ('one', 4465), ('how', 4308), ('feel', 4239), ('there', 4218), ('him', 3988), ('good', 3964), ('here', 3837), ('more', 3810), ('some', 3762), ('had', 3759), ('from', 3684), ('need', 3580), ('been', 3560), ('right', 3428), ('them', 3346), ('did', 3323), ('ca', 3261), ('too', 3213), ('has', 3200), ('could', 3131), ('things', 3117), ('My', 3075), ('well', 3012), ('school', 3010), ('class', 2983), ('wonder', 2959), ('see', 2882), ('should', 2836), ('guess', 2811), ('friends', 2804), ('back', 2684), ('something', 2657), ('very', 2648)]\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "viz.most_frequent_words()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "hidden": true
   },
   "source": [
    "##### Most frequent words in the preprocessed corpus"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "hidden": true
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "viz.most_frequent_words_preprocessed()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "hidden": true
   },
   "source": [
    "##### Statistics on the text corpus"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "metadata": {
    "hidden": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The total number of essays is 2467\n",
      "The total number of words in all essays is 1608813\n",
      "The average number of words in each essay is 652.1333603567085\n"
     ]
    }
   ],
   "source": [
    "viz.get_corpus_statistics()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "hidden": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The average number of words in each preprocessed essay is 167.26509931090393\n",
      "The standard deviation of the number of words in each preprocessed essay is 62.85333564580388\n",
      "The average number of words in each preprocessed essay plus 2 standard deviations is 292.9717706025117\n"
     ]
    }
   ],
   "source": [
    "viz.get_preprocessed_corpus_statistics()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {
    "hidden": true
   },
   "outputs": [
    {
     "ename": "KeyError",
     "evalue": "8791",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mKeyError\u001b[0m                                  Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-88-e69c006f594b>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0mviz_tsne\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtsne\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mX_essays\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmax_features\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m30000\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmax_sentence_len\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m300\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0membed_dim\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m300\u001b[0m\u001b[0;34m,\u001b[0m  \u001b[0mn_elements\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m100\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mviz_tsne\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mplot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m<ipython-input-87-63a7fd2ef34c>\u001b[0m in \u001b[0;36mplot\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m    162\u001b[0m         \u001b[0;32mfor\u001b[0m \u001b[0mindex\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrandom\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msample\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ml_bound\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mu_bound\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_elements\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    163\u001b[0m             \u001b[0mtokens\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvectors\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 164\u001b[0;31m             \u001b[0mlabels\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwords\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    165\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    166\u001b[0m         \u001b[0mtsne_model\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mTSNE\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mperplexity\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m40\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn_components\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minit\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'pca'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn_iter\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m2500\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrandom_state\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m23\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mKeyError\u001b[0m: 8791"
     ]
    }
   ],
   "source": [
    "viz_tsne = tsne(X_essays, max_features = 30000, max_sentence_len = 300, embed_dim = 300,  n_elements = 100)\n",
    "viz_tsne.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Train models"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Train Keras model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {},
   "outputs": [],
   "source": [
    "class train:\n",
    "\n",
    "    def __init__(self, corpus):\n",
    "        self.max_sentence_len = 300\n",
    "        self.max_features = 300\n",
    "        self.embed_dim = 300\n",
    "        self.lstm_out = 180\n",
    "        self.dropout_lstm = 0.3\n",
    "        self.recurrent_dropout_lstm = 0.3\n",
    "        self.dropout = 0.3\n",
    "        self.conv_nfilters = 128\n",
    "        self.conv_kernel_size = 8\n",
    "        self.max_pool_size = 2\n",
    "        self.NLTKPreprocessor = self.NLTKPreprocessor(corpus)\n",
    "        #self.MyRNNTransformer = self.MyRNNTransformer()\n",
    "\n",
    "\n",
    "    class NLTKPreprocessor(BaseEstimator, TransformerMixin):\n",
    "        \"\"\"\n",
    "        Transforms input data by using NLTK tokenization, POS tagging, lemmatization and vectorization.\n",
    "        \"\"\"\n",
    "\n",
    "        def __init__(self, corpus, max_sentence_len = 300, stopwords=None, punct=None, lower=True, strip=True):\n",
    "            \"\"\"\n",
    "            Instantiates the preprocessor.\n",
    "            \"\"\"\n",
    "            self.lower = lower\n",
    "            self.strip = strip\n",
    "            self.stopwords = set(stopwords) if stopwords else set(sw.words('english'))\n",
    "            self.punct = set(punct) if punct else set(string.punctuation)\n",
    "            self.lemmatizer = WordNetLemmatizer()\n",
    "            self.corpus = corpus\n",
    "            self.max_sentence_len = max_sentence_len\n",
    "\n",
    "        def fit(self, X, y=None):\n",
    "            \"\"\"\n",
    "            Fit simply returns self.\n",
    "            \"\"\"\n",
    "            return self\n",
    "\n",
    "        def inverse_transform(self, X):\n",
    "            \"\"\"\n",
    "            No inverse transformation.\n",
    "            \"\"\"\n",
    "            return X\n",
    "\n",
    "        def transform(self, X):\n",
    "            \"\"\"\n",
    "            Actually runs the preprocessing on each document.\n",
    "            \"\"\"\n",
    "            output = np.array([(self.tokenize(doc)) for doc in X])\n",
    "            return output\n",
    "\n",
    "        def tokenize(self, document):\n",
    "            \"\"\"\n",
    "            Returns a normalized, lemmatized list of tokens from a document by\n",
    "            applying segmentation, tokenization, and part of speech tagging.\n",
    "            Uses the part of speech tags to look up the lemma in WordNet, and returns the lowercase\n",
    "            version of all the words, removing stopwords and punctuation.\n",
    "            \"\"\"\n",
    "            lemmatized_tokens = []\n",
    "\n",
    "            # Clean the text\n",
    "            document = re.sub(r\"[^A-Za-z0-9^,!.\\/'+-=]\", \" \", document)\n",
    "            document = re.sub(r\"what's\", \"what is \", document)\n",
    "            document = re.sub(r\"\\'s\", \" \", document)\n",
    "            document = re.sub(r\"\\'ve\", \" have \", document)\n",
    "            document = re.sub(r\"can't\", \"cannot \", document)\n",
    "            document = re.sub(r\"n't\", \" not \", document)\n",
    "            document = re.sub(r\"i'm\", \"i am \", document)\n",
    "            document = re.sub(r\"\\'re\", \" are \", document)\n",
    "            document = re.sub(r\"\\'d\", \" would \", document)\n",
    "            document = re.sub(r\"\\'ll\", \" will \", document)\n",
    "            document = re.sub(r\"(\\d+)(k)\", r\"\\g<1>000\", document)\n",
    "\n",
    "            # Break the document into sentences\n",
    "            for sent in sent_tokenize(document):\n",
    "\n",
    "                # Break the sentence into part of speech tagged tokens\n",
    "                for token, tag in pos_tag(wordpunct_tokenize(sent)):\n",
    "\n",
    "                    # Apply preprocessing to the token\n",
    "                    token = token.lower() if self.lower else token\n",
    "                    token = token.strip() if self.strip else token\n",
    "                    token = token.strip('_') if self.strip else token\n",
    "                    token = token.strip('*') if self.strip else token\n",
    "\n",
    "                    # If punctuation or stopword, ignore token and continue\n",
    "                    if token in self.stopwords or all(char in self.punct for char in token):\n",
    "                        continue\n",
    "\n",
    "                    # Lemmatize the token\n",
    "                    lemma = self.lemmatize(token, tag)\n",
    "                    lemmatized_tokens.append(lemma)\n",
    "\n",
    "            doc = ' '.join(lemmatized_tokens)\n",
    "            tokenized_document = self.vectorize(np.array(doc)[np.newaxis])\n",
    "            return tokenized_document\n",
    "\n",
    "\n",
    "        def vectorize(self, doc):\n",
    "            \"\"\"\n",
    "            Returns a vectorized padded version of sequences.\n",
    "            \"\"\"\n",
    "            save_path = \"./Data/padding.pickle\"\n",
    "            with open(save_path, 'rb') as f:\n",
    "                tokenizer = pickle.load(f)\n",
    "            doc_pad = tokenizer.texts_to_sequences(doc)\n",
    "            doc_pad = pad_sequences(doc_pad, padding='pre', truncating='pre', maxlen=self.max_sentence_len)\n",
    "            return np.squeeze(doc_pad)\n",
    "\n",
    "        def lemmatize(self, token, tag):\n",
    "            \"\"\"\n",
    "            Converts the Penn Treebank tag to a WordNet POS tag, then uses that\n",
    "            tag to perform WordNet lemmatization.\n",
    "            \"\"\"\n",
    "            tag = {\n",
    "                'N': wn.NOUN,\n",
    "                'V': wn.VERB,\n",
    "                'R': wn.ADV,\n",
    "                'J': wn.ADJ\n",
    "            }.get(tag[0], wn.NOUN)\n",
    "\n",
    "            return self.lemmatizer.lemmatize(token, tag)\n",
    "\n",
    "\n",
    "    class MyRNNTransformer(BaseEstimator, TransformerMixin):\n",
    "        \"\"\"\n",
    "        Transformer allowing our Keras model to be included in our pipeline\n",
    "        \"\"\"\n",
    "        def __init__(self, classifier):\n",
    "            self.classifier = classifier\n",
    "\n",
    "        def fit(self, X, y):\n",
    "            batch_size = 32\n",
    "            num_epochs = 135\n",
    "            batch_size = batch_size\n",
    "            epochs = num_epochs\n",
    "            self.classifier.fit(X, y, epochs=epochs, batch_size=batch_size, verbose=2)\n",
    "            return self\n",
    "\n",
    "        def transform(self, X):\n",
    "            self.pred = self.classifier.predict(X)\n",
    "            self.classes = [[0 if el < 0.2 else 1 for el in item] for item in self.pred]\n",
    "            return self.classes\n",
    "\n",
    "\n",
    "    def multiclass_accuracy(self,predictions, target):\n",
    "        \"Returns the multiclass accuracy of the classifier's predictions\"\n",
    "        score = []\n",
    "        for j in range(0, 5):\n",
    "            count = 0\n",
    "            for i in range(len(predictions)):\n",
    "                if predictions[i][j] == target[i][j]:\n",
    "                    count += 1\n",
    "            score.append(count / len(predictions))\n",
    "        return score\n",
    "\n",
    "\n",
    "    def load_google_vec(self):\n",
    "        url = 'https://s3.amazonaws.com/dl4j-distribution/GoogleNews-vectors-negative300.bin.gz'\n",
    "        #wget.download(url, 'Data/GoogleNews-vectors.bin.gz')\n",
    "        return KeyedVectors.load_word2vec_format(\n",
    "            'Data/GoogleNews-vectors.bin.gz',\n",
    "            binary=True)\n",
    "\n",
    "    def lemmatize_token(self, token, tag):\n",
    "        tag = {\n",
    "            'N': wn.NOUN,\n",
    "            'V': wn.VERB,\n",
    "            'R': wn.ADV,\n",
    "            'J': wn.ADJ\n",
    "        }.get(tag[0], wn.NOUN)\n",
    "        return WordNetLemmatizer().lemmatize(token, tag)\n",
    "\n",
    "\n",
    "    def get_preprocessed_corpus(self, X_corpus):\n",
    "        \"\"\"\n",
    "        Returns a preprocessed version of a full corpus (ie. tokenization and lemmatization using POS taggs)\n",
    "        \"\"\"\n",
    "        X = ' '.join(X_corpus)\n",
    "        lemmatized_tokens = []\n",
    "\n",
    "        # Break the document into sentences\n",
    "        for sent in sent_tokenize(X):\n",
    "\n",
    "            # Break the sentence into part of speech tagged tokens\n",
    "            for token, tag in pos_tag(wordpunct_tokenize(sent)):\n",
    "\n",
    "                # Apply preprocessing to the token\n",
    "                token = token.lower()\n",
    "                token = token.strip()\n",
    "                token = token.strip('_')\n",
    "                token = token.strip('*')\n",
    "\n",
    "                # If punctuation or stopword, ignore token and continue\n",
    "                if token in set(sw.words('english')) or all(char in set(string.punctuation) for char in token):\n",
    "                    continue\n",
    "\n",
    "                # Lemmatize the token and yield\n",
    "                lemma = self.lemmatize_token(token, tag)\n",
    "                lemmatized_tokens.append(lemma)\n",
    "\n",
    "        doc = ' '.join(lemmatized_tokens)\n",
    "        return doc\n",
    "\n",
    "\n",
    "    def prepare_embedding(self, X):\n",
    "        \"\"\"\n",
    "        Returns the embedding weights matrix, the word index, and the word-vector dictionnary corresponding\n",
    "        to the training corpus set of words.\n",
    "        \"\"\"\n",
    "        # Load Word2Vec vectors\n",
    "        word2vec = self.load_google_vec()\n",
    "\n",
    "        # Fit and apply an NLTK tokenizer on the preprocessed training corpus to obtain sequences.\n",
    "        tokenizer = Tokenizer(num_words=self.max_features)\n",
    "        X_pad = self.get_preprocessed_corpus(X)\n",
    "        tokenizer.fit_on_texts(pd.Series(X_pad))\n",
    "        X_pad = tokenizer.texts_to_sequences(pd.Series(X_pad))\n",
    "\n",
    "        # Pad the sequences\n",
    "        X_pad = pad_sequences(X_pad, maxlen=self.max_sentence_len, padding='post', truncating='post')\n",
    "\n",
    "        # Retrieve the word index\n",
    "        train_word_index = tokenizer.word_index\n",
    "\n",
    "        # Construct the embedding weights matrix and word-vector dictionnary\n",
    "        train_embedding_weights = np.zeros((len(train_word_index) + 1, self.embed_dim))\n",
    "        for word, index in train_word_index.items():\n",
    "            train_embedding_weights[index, :] = word2vec[word] if word in word2vec else np.random.rand(self.embed_dim)\n",
    "        word_vector_dict = dict(zip(pd.Series(list(train_word_index.keys())),\n",
    "                                    pd.Series(list(train_word_index.keys())).apply(\n",
    "                                        lambda x: train_embedding_weights[train_word_index[x]])))\n",
    "        return train_embedding_weights, train_word_index, word_vector_dict\n",
    "\n",
    "\n",
    "    def run(self, X, y, model_name=None, pretrained_weights_path = None, pretrained_model_path = None, verbose=True):\n",
    "        \"\"\"\n",
    "        Builds a classifer for the given list of documents and targets\n",
    "\n",
    "        \"\"\"\n",
    "\n",
    "        def build(classifier, X, y, embedding_dict, corpus):\n",
    "            \"\"\"\n",
    "            Inner build function that builds a pipeline including a preprocessor and a classifier.\n",
    "            \"\"\"\n",
    "            model = Pipeline([\n",
    "                ('preprocessor', self.NLTKPreprocessor),\n",
    "                ('classifier', classifier)\n",
    "            ])\n",
    "            return model.fit(X, y)\n",
    "\n",
    "        # Label encode the targets\n",
    "        y_trans = y\n",
    "\n",
    "        # Prepare the embedding\n",
    "        train_embedding_weights, train_word_index, wv_dict = self.prepare_embedding(X)\n",
    "\n",
    "        # Begin evaluation\n",
    "        if verbose: print(\"Building for evaluation\")\n",
    "        indices = range(len(y))\n",
    "\n",
    "        # Keras model definition\n",
    "        Input_words = Input(shape=(300,), name='input1')\n",
    "        x = Embedding(len(train_word_index) + 1, self.embed_dim, weights=[train_embedding_weights],\n",
    "                      input_length=self.max_sentence_len, trainable=True)(Input_words)\n",
    "        # classifier.add(Embedding(30000, 300,input_length = 350))\n",
    "        x = Conv1D(filters=self.conv_nfilters, kernel_size= self.conv_kernel_size, padding='same', activation='relu')(x)\n",
    "        x = MaxPooling1D(pool_size=self.max_pool_size)(x)\n",
    "        x = SpatialDropout1D(self.dropout)(x)\n",
    "        x = BatchNormalization()(x)\n",
    "        x = Conv1D(filters=(self.conv_nfilters)*2, kernel_size= self.conv_kernel_size, padding='same', activation='relu')(x)\n",
    "        x = MaxPooling1D(pool_size=self.max_pool_size)(x)\n",
    "        x = SpatialDropout1D(self.dropout)(x)\n",
    "        x = BatchNormalization()(x)\n",
    "        x = Conv1D(filters=(self.conv_nfilters)*3, kernel_size= self.conv_kernel_size, padding='same', activation='relu')(x)\n",
    "        x = MaxPooling1D(pool_size=self.max_pool_size)(x)\n",
    "        x = SpatialDropout1D(self.dropout)(x)\n",
    "        x = BatchNormalization()(x)\n",
    "        x = LSTM(self.lstm_out, return_sequences=True, dropout=self.dropout_lstm, recurrent_dropout=self.recurrent_dropout_lstm)(x)\n",
    "        x = LSTM(self.lstm_out, return_sequences=True, dropout=self.dropout_lstm, recurrent_dropout=self.recurrent_dropout_lstm)(x)\n",
    "        x = LSTM(self.lstm_out, dropout=self.dropout_lstm, recurrent_dropout=self.recurrent_dropout_lstm)(x)\n",
    "        x = Dense(128, activation='softmax')(x)\n",
    "        out = Dense(5, activation='softmax')(x)\n",
    "        classifier = Model(inputs=Input_words, outputs=[out])\n",
    "        classifier.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])\n",
    "        print(classifier.summary())\n",
    "\n",
    "        # Loading pretrained model for transfer learning\n",
    "        if pretrained_weights_path and pretrained_model_path:\n",
    "            json_file = open(pretrained_model_path, 'r')\n",
    "            classifier = model_from_json(json_file.read())\n",
    "            classifier.load_weights(pretrained_weights_path)\n",
    "            classifier.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])\n",
    "            json_file.close()\n",
    "            model = build(self.MyRNNTransformer(classifier), X, y_trans, wv_dict, corpus=X)\n",
    "            \n",
    "        # Train on the whole set from scratch\n",
    "        if verbose: \n",
    "            print(\"Building complete model and saving ...\")\n",
    "            model= build(self.MyRNNTransformer(classifier), X, y_trans, wv_dict, corpus=X)\n",
    "\n",
    "        # Save the model\n",
    "        if model_name:\n",
    "            outpath = './Models/'\n",
    "            classifier.save_weights(outpath + model_name + '.h5')\n",
    "            with open(outpath + model_name + '.json', 'w') as json_file:\n",
    "                json_file.write(classifier.to_json())\n",
    "            print(\"Model written out to {}\".format(model_name))\n",
    "        else:\n",
    "            print('Please provide model name for saving')\n",
    "        \n",
    "        return model\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building for evaluation\n",
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "input1 (InputLayer)          (None, 300)               0         \n",
      "_________________________________________________________________\n",
      "embedding_15 (Embedding)     (None, 300, 300)          6704100   \n",
      "_________________________________________________________________\n",
      "conv1d_38 (Conv1D)           (None, 300, 128)          307328    \n",
      "_________________________________________________________________\n",
      "max_pooling1d_38 (MaxPooling (None, 150, 128)          0         \n",
      "_________________________________________________________________\n",
      "spatial_dropout1d_38 (Spatia (None, 150, 128)          0         \n",
      "_________________________________________________________________\n",
      "batch_normalization_38 (Batc (None, 150, 128)          512       \n",
      "_________________________________________________________________\n",
      "conv1d_39 (Conv1D)           (None, 150, 256)          262400    \n",
      "_________________________________________________________________\n",
      "max_pooling1d_39 (MaxPooling (None, 75, 256)           0         \n",
      "_________________________________________________________________\n",
      "spatial_dropout1d_39 (Spatia (None, 75, 256)           0         \n",
      "_________________________________________________________________\n",
      "batch_normalization_39 (Batc (None, 75, 256)           1024      \n",
      "_________________________________________________________________\n",
      "conv1d_40 (Conv1D)           (None, 75, 384)           786816    \n",
      "_________________________________________________________________\n",
      "max_pooling1d_40 (MaxPooling (None, 37, 384)           0         \n",
      "_________________________________________________________________\n",
      "spatial_dropout1d_40 (Spatia (None, 37, 384)           0         \n",
      "_________________________________________________________________\n",
      "batch_normalization_40 (Batc (None, 37, 384)           1536      \n",
      "_________________________________________________________________\n",
      "lstm_38 (LSTM)               (None, 37, 180)           406800    \n",
      "_________________________________________________________________\n",
      "lstm_39 (LSTM)               (None, 37, 180)           259920    \n",
      "_________________________________________________________________\n",
      "lstm_40 (LSTM)               (None, 180)               259920    \n",
      "_________________________________________________________________\n",
      "dense_24 (Dense)             (None, 128)               23168     \n",
      "_________________________________________________________________\n",
      "dense_25 (Dense)             (None, 5)                 645       \n",
      "=================================================================\n",
      "Total params: 9,014,169\n",
      "Trainable params: 9,012,633\n",
      "Non-trainable params: 1,536\n",
      "_________________________________________________________________\n",
      "None\n",
      "Building complete model and saving ...\n",
      "Epoch 1/135\n",
      " - 43s - loss: 4.1201 - acc: 0.0851\n",
      "Epoch 2/135\n",
      " - 31s - loss: 4.1199 - acc: 0.1049\n",
      "Epoch 3/135\n",
      " - 31s - loss: 4.1149 - acc: 0.0806\n",
      "Epoch 4/135\n",
      " - 31s - loss: 4.1034 - acc: 0.0720\n",
      "Epoch 5/135\n",
      " - 31s - loss: 4.0920 - acc: 0.0715\n",
      "Epoch 6/135\n",
      " - 31s - loss: 4.0858 - acc: 0.0725\n",
      "Epoch 7/135\n",
      " - 31s - loss: 4.0781 - acc: 0.0542\n",
      "Epoch 8/135\n",
      " - 31s - loss: 4.0630 - acc: 0.0735\n",
      "Epoch 9/135\n",
      " - 31s - loss: 4.0703 - acc: 0.0552\n",
      "Epoch 10/135\n",
      " - 31s - loss: 4.0597 - acc: 0.0558\n",
      "Epoch 11/135\n",
      " - 31s - loss: 4.0445 - acc: 0.0679\n",
      "Epoch 12/135\n",
      " - 31s - loss: 4.0452 - acc: 0.0983\n",
      "Epoch 13/135\n",
      " - 31s - loss: 4.0321 - acc: 0.0623\n",
      "Epoch 14/135\n",
      " - 31s - loss: 4.0244 - acc: 0.0806\n",
      "Epoch 15/135\n",
      " - 31s - loss: 4.0262 - acc: 0.0628\n",
      "Epoch 16/135\n",
      " - 30s - loss: 4.0115 - acc: 0.0831\n",
      "Epoch 17/135\n",
      " - 31s - loss: 4.0098 - acc: 0.0644\n",
      "Epoch 18/135\n",
      " - 31s - loss: 4.0019 - acc: 0.0801\n",
      "Epoch 19/135\n",
      " - 31s - loss: 3.9871 - acc: 0.0801\n",
      "Epoch 20/135\n",
      " - 31s - loss: 3.9806 - acc: 0.0821\n",
      "Epoch 21/135\n",
      " - 31s - loss: 3.9701 - acc: 0.0664\n",
      "Epoch 22/135\n",
      " - 31s - loss: 3.9675 - acc: 0.0745\n",
      "Epoch 23/135\n",
      " - 31s - loss: 3.9613 - acc: 0.0689\n",
      "Epoch 24/135\n",
      " - 31s - loss: 3.9619 - acc: 0.0816\n",
      "Epoch 25/135\n",
      " - 31s - loss: 3.9502 - acc: 0.0730\n",
      "Epoch 26/135\n",
      " - 31s - loss: 3.9464 - acc: 0.0563\n",
      "Epoch 27/135\n",
      " - 31s - loss: 3.9639 - acc: 0.0644\n",
      "Epoch 28/135\n",
      " - 31s - loss: 3.9539 - acc: 0.0542\n",
      "Epoch 29/135\n",
      " - 31s - loss: 3.9504 - acc: 0.0588\n",
      "Epoch 30/135\n",
      " - 31s - loss: 3.9384 - acc: 0.0684\n",
      "Epoch 31/135\n",
      " - 31s - loss: 3.9281 - acc: 0.0740\n",
      "Epoch 32/135\n",
      " - 31s - loss: 3.9266 - acc: 0.0730\n",
      "Epoch 33/135\n",
      " - 31s - loss: 3.9192 - acc: 0.0998\n",
      "Epoch 34/135\n",
      " - 31s - loss: 3.9079 - acc: 0.1014\n",
      "Epoch 35/135\n",
      " - 31s - loss: 3.9082 - acc: 0.0669\n",
      "Epoch 36/135\n",
      " - 31s - loss: 3.9297 - acc: 0.0517\n",
      "Epoch 37/135\n",
      " - 31s - loss: 3.9291 - acc: 0.0867\n",
      "Epoch 38/135\n",
      " - 31s - loss: 3.9115 - acc: 0.0877\n",
      "Epoch 39/135\n",
      " - 31s - loss: 3.9018 - acc: 0.0851\n",
      "Epoch 40/135\n",
      " - 31s - loss: 3.8890 - acc: 0.0998\n",
      "Epoch 41/135\n",
      " - 31s - loss: 3.8826 - acc: 0.0487\n",
      "Epoch 42/135\n",
      " - 31s - loss: 3.8777 - acc: 0.0598\n",
      "Epoch 43/135\n",
      " - 31s - loss: 3.8780 - acc: 0.0786\n",
      "Epoch 44/135\n",
      " - 31s - loss: 3.8916 - acc: 0.0634\n",
      "Epoch 45/135\n",
      " - 31s - loss: 3.8644 - acc: 0.0492\n",
      "Epoch 46/135\n",
      " - 31s - loss: 3.8715 - acc: 0.0487\n",
      "Epoch 47/135\n",
      " - 32s - loss: 3.8752 - acc: 0.0613\n",
      "Epoch 48/135\n",
      " - 31s - loss: 3.8727 - acc: 0.0593\n",
      "Epoch 49/135\n",
      " - 31s - loss: 3.8569 - acc: 0.0552\n",
      "Epoch 50/135\n",
      " - 31s - loss: 3.8529 - acc: 0.0487\n",
      "Epoch 51/135\n",
      " - 31s - loss: 3.8403 - acc: 0.0482\n",
      "Epoch 52/135\n",
      " - 31s - loss: 3.8298 - acc: 0.0497\n",
      "Epoch 53/135\n",
      " - 31s - loss: 3.8438 - acc: 0.0441\n",
      "Epoch 54/135\n",
      " - 31s - loss: 3.8401 - acc: 0.0466\n",
      "Epoch 55/135\n",
      " - 31s - loss: 3.8324 - acc: 0.0547\n",
      "Epoch 56/135\n",
      " - 31s - loss: 3.8261 - acc: 0.0532\n",
      "Epoch 57/135\n",
      " - 31s - loss: 3.8250 - acc: 0.0588\n",
      "Epoch 58/135\n",
      " - 31s - loss: 3.8339 - acc: 0.0517\n",
      "Epoch 59/135\n",
      " - 31s - loss: 3.8099 - acc: 0.0517\n",
      "Epoch 60/135\n",
      " - 31s - loss: 3.8169 - acc: 0.0497\n",
      "Epoch 61/135\n",
      " - 31s - loss: 3.7963 - acc: 0.0568\n",
      "Epoch 62/135\n",
      " - 31s - loss: 3.7945 - acc: 0.0608\n",
      "Epoch 63/135\n",
      " - 31s - loss: 3.7911 - acc: 0.0532\n",
      "Epoch 64/135\n",
      " - 31s - loss: 3.7930 - acc: 0.0705\n",
      "Epoch 65/135\n",
      " - 31s - loss: 3.7777 - acc: 0.0628\n",
      "Epoch 66/135\n",
      " - 31s - loss: 3.7864 - acc: 0.0537\n",
      "Epoch 67/135\n",
      " - 31s - loss: 3.7838 - acc: 0.0922\n",
      "Epoch 68/135\n",
      " - 31s - loss: 3.7622 - acc: 0.1232\n",
      "Epoch 69/135\n",
      " - 31s - loss: 3.7530 - acc: 0.1368\n",
      "Epoch 70/135\n",
      " - 31s - loss: 3.7449 - acc: 0.1485\n",
      "Epoch 71/135\n",
      " - 31s - loss: 3.7546 - acc: 0.1617\n",
      "Epoch 72/135\n",
      " - 31s - loss: 3.7419 - acc: 0.1581\n",
      "Epoch 73/135\n",
      " - 31s - loss: 3.7412 - acc: 0.1571\n",
      "Epoch 74/135\n",
      " - 31s - loss: 3.7319 - acc: 0.1617\n",
      "Epoch 75/135\n",
      " - 31s - loss: 3.7228 - acc: 0.1561\n",
      "Epoch 76/135\n",
      " - 31s - loss: 3.7316 - acc: 0.1662\n",
      "Epoch 77/135\n",
      " - 30s - loss: 3.7204 - acc: 0.1591\n",
      "Epoch 78/135\n",
      " - 30s - loss: 3.7153 - acc: 0.1647\n",
      "Epoch 79/135\n",
      " - 30s - loss: 3.7143 - acc: 0.1718\n",
      "Epoch 80/135\n",
      " - 30s - loss: 3.7041 - acc: 0.1718\n",
      "Epoch 81/135\n",
      " - 30s - loss: 3.6984 - acc: 0.1784\n",
      "Epoch 82/135\n",
      " - 30s - loss: 3.7022 - acc: 0.1723\n",
      "Epoch 83/135\n",
      " - 30s - loss: 3.6877 - acc: 0.1855\n",
      "Epoch 84/135\n",
      " - 30s - loss: 3.6916 - acc: 0.1951\n",
      "Epoch 85/135\n",
      " - 30s - loss: 3.6699 - acc: 0.2007\n",
      "Epoch 86/135\n",
      " - 31s - loss: 3.6793 - acc: 0.1921\n",
      "Epoch 87/135\n",
      " - 31s - loss: 3.6718 - acc: 0.2048\n",
      "Epoch 88/135\n",
      " - 31s - loss: 3.6705 - acc: 0.2032\n",
      "Epoch 89/135\n",
      " - 31s - loss: 3.6612 - acc: 0.1896\n",
      "Epoch 90/135\n",
      " - 31s - loss: 3.6579 - acc: 0.2083\n",
      "Epoch 91/135\n",
      " - 31s - loss: 3.6467 - acc: 0.1921\n",
      "Epoch 92/135\n",
      " - 31s - loss: 3.6403 - acc: 0.2002\n",
      "Epoch 93/135\n",
      " - 31s - loss: 3.6374 - acc: 0.2119\n",
      "Epoch 94/135\n",
      " - 31s - loss: 3.6449 - acc: 0.2002\n",
      "Epoch 95/135\n",
      " - 31s - loss: 3.6302 - acc: 0.2103\n",
      "Epoch 96/135\n",
      " - 31s - loss: 3.6361 - acc: 0.2038\n",
      "Epoch 97/135\n",
      " - 31s - loss: 3.6254 - acc: 0.2063\n",
      "Epoch 98/135\n",
      " - 31s - loss: 3.6245 - acc: 0.2027\n",
      "Epoch 99/135\n",
      " - 31s - loss: 3.6166 - acc: 0.2124\n",
      "Epoch 100/135\n",
      " - 31s - loss: 3.6158 - acc: 0.2164\n",
      "Epoch 101/135\n",
      " - 31s - loss: 3.6134 - acc: 0.2250\n",
      "Epoch 102/135\n",
      " - 31s - loss: 3.6061 - acc: 0.2184\n",
      "Epoch 103/135\n",
      " - 31s - loss: 3.5938 - acc: 0.2225\n",
      "Epoch 104/135\n",
      " - 31s - loss: 3.6002 - acc: 0.2235\n",
      "Epoch 105/135\n",
      " - 31s - loss: 3.6005 - acc: 0.2190\n",
      "Epoch 106/135\n",
      " - 31s - loss: 3.5953 - acc: 0.2377\n",
      "Epoch 107/135\n",
      " - 31s - loss: 3.5998 - acc: 0.2352\n",
      "Epoch 108/135\n",
      " - 31s - loss: 3.5917 - acc: 0.2291\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 109/135\n",
      " - 31s - loss: 3.5640 - acc: 0.2255\n",
      "Epoch 110/135\n",
      " - 31s - loss: 3.5813 - acc: 0.2357\n",
      "Epoch 111/135\n",
      " - 31s - loss: 3.5756 - acc: 0.2286\n",
      "Epoch 112/135\n",
      " - 31s - loss: 3.5669 - acc: 0.2321\n",
      "Epoch 113/135\n",
      " - 32s - loss: 3.5592 - acc: 0.2342\n",
      "Epoch 114/135\n",
      " - 31s - loss: 3.5553 - acc: 0.2494\n",
      "Epoch 115/135\n",
      " - 31s - loss: 3.5499 - acc: 0.2402\n",
      "Epoch 116/135\n",
      " - 31s - loss: 3.5548 - acc: 0.2342\n",
      "Epoch 117/135\n",
      " - 31s - loss: 3.5595 - acc: 0.2438\n",
      "Epoch 118/135\n",
      " - 31s - loss: 3.5493 - acc: 0.2321\n",
      "Epoch 119/135\n",
      " - 31s - loss: 3.5516 - acc: 0.2478\n",
      "Epoch 120/135\n",
      " - 31s - loss: 3.5398 - acc: 0.2443\n",
      "Epoch 121/135\n",
      " - 31s - loss: 3.5323 - acc: 0.2544\n",
      "Epoch 122/135\n",
      " - 31s - loss: 3.5171 - acc: 0.2712\n",
      "Epoch 123/135\n",
      " - 31s - loss: 3.5237 - acc: 0.2509\n",
      "Epoch 124/135\n",
      " - 31s - loss: 3.5163 - acc: 0.2514\n",
      "Epoch 125/135\n",
      " - 31s - loss: 3.5018 - acc: 0.2539\n",
      "Epoch 126/135\n",
      " - 31s - loss: 3.5033 - acc: 0.2408\n",
      "Epoch 127/135\n",
      " - 31s - loss: 3.4943 - acc: 0.2499\n",
      "Epoch 128/135\n",
      " - 31s - loss: 3.4912 - acc: 0.2549\n",
      "Epoch 129/135\n",
      " - 31s - loss: 3.4811 - acc: 0.2549\n",
      "Epoch 130/135\n",
      " - 31s - loss: 3.4799 - acc: 0.2331\n",
      "Epoch 131/135\n",
      " - 31s - loss: 3.4901 - acc: 0.2230\n",
      "Epoch 132/135\n",
      " - 31s - loss: 3.4707 - acc: 0.2519\n",
      "Epoch 133/135\n",
      " - 31s - loss: 3.4741 - acc: 0.2286\n",
      "Epoch 134/135\n",
      " - 32s - loss: 3.4758 - acc: 0.2367\n",
      "Epoch 135/135\n",
      " - 32s - loss: 3.4566 - acc: 0.2372\n",
      "Model written out to Personality_traits_NN\n"
     ]
    }
   ],
   "source": [
    "model = train(X_train).run(X_train, y_train, \"Personality_traits_NN\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Train SVM"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "metadata": {},
   "outputs": [],
   "source": [
    "class train_svm:\n",
    "\n",
    "    def __init__(self, corpus):\n",
    "        self.max_sentence_len = 300\n",
    "        self.max_features = 300\n",
    "        self.embed_dim = 300\n",
    "        self.NLTKPreprocessor = self.NLTKPreprocessor(corpus)\n",
    "        #self.MyRNNTransformer = self.MyRNNTransformer()\n",
    "\n",
    "\n",
    "    class NLTKPreprocessor(BaseEstimator, TransformerMixin):\n",
    "        \"\"\"\n",
    "        Transforms input data by using NLTK tokenization, POS tagging, lemmatization and vectorization.\n",
    "        \"\"\"\n",
    "\n",
    "        def __init__(self, corpus, max_sentence_len = 300, stopwords=None, punct=None, lower=True, strip=True):\n",
    "            \"\"\"\n",
    "            Instantiates the preprocessor.\n",
    "            \"\"\"\n",
    "            self.lower = lower\n",
    "            self.strip = strip\n",
    "            self.stopwords = set(stopwords) if stopwords else set(sw.words('english'))\n",
    "            self.punct = set(punct) if punct else set(string.punctuation)\n",
    "            self.lemmatizer = WordNetLemmatizer()\n",
    "            self.corpus = corpus\n",
    "            self.max_sentence_len = max_sentence_len\n",
    "\n",
    "        def fit(self, X, y=None):\n",
    "            \"\"\"\n",
    "            Fit simply returns self.\n",
    "            \"\"\"\n",
    "            return self\n",
    "\n",
    "        def inverse_transform(self, X):\n",
    "            \"\"\"\n",
    "            No inverse transformation.\n",
    "            \"\"\"\n",
    "            return X\n",
    "\n",
    "        def transform(self, X):\n",
    "            \"\"\"\n",
    "            Actually runs the preprocessing on each document.\n",
    "            \"\"\"\n",
    "            output = np.array([(self.tokenize(doc)) for doc in X])\n",
    "            return output\n",
    "\n",
    "        def tokenize(self, document):\n",
    "            \"\"\"\n",
    "            Returns a normalized, lemmatized list of tokens from a document by\n",
    "            applying segmentation, tokenization, and part of speech tagging.\n",
    "            Uses the part of speech tags to look up the lemma in WordNet, and returns the lowercase\n",
    "            version of all the words, removing stopwords and punctuation.\n",
    "            \"\"\"\n",
    "            lemmatized_tokens = []\n",
    "\n",
    "            # Clean the text\n",
    "            document = re.sub(r\"[^A-Za-z0-9^,!.\\/'+-=]\", \" \", document)\n",
    "            document = re.sub(r\"what's\", \"what is \", document)\n",
    "            document = re.sub(r\"\\'s\", \" \", document)\n",
    "            document = re.sub(r\"\\'ve\", \" have \", document)\n",
    "            document = re.sub(r\"can't\", \"cannot \", document)\n",
    "            document = re.sub(r\"n't\", \" not \", document)\n",
    "            document = re.sub(r\"i'm\", \"i am \", document)\n",
    "            document = re.sub(r\"\\'re\", \" are \", document)\n",
    "            document = re.sub(r\"\\'d\", \" would \", document)\n",
    "            document = re.sub(r\"\\'ll\", \" will \", document)\n",
    "            document = re.sub(r\"(\\d+)(k)\", r\"\\g<1>000\", document)\n",
    "\n",
    "            # Break the document into sentences\n",
    "            for sent in sent_tokenize(document):\n",
    "\n",
    "                # Break the sentence into part of speech tagged tokens\n",
    "                for token, tag in pos_tag(wordpunct_tokenize(sent)):\n",
    "\n",
    "                    # Apply preprocessing to the token\n",
    "                    token = token.lower() if self.lower else token\n",
    "                    token = token.strip() if self.strip else token\n",
    "                    token = token.strip('_') if self.strip else token\n",
    "                    token = token.strip('*') if self.strip else token\n",
    "\n",
    "                    # If punctuation or stopword, ignore token and continue\n",
    "                    if token in self.stopwords or all(char in self.punct for char in token):\n",
    "                        continue\n",
    "\n",
    "                    # Lemmatize the token\n",
    "                    lemma = self.lemmatize(token, tag)\n",
    "                    lemmatized_tokens.append(lemma)\n",
    "\n",
    "            doc = ' '.join(lemmatized_tokens)\n",
    "            tokenized_document = self.vectorize(np.array(doc)[np.newaxis])\n",
    "            return tokenized_document\n",
    "\n",
    "\n",
    "        def vectorize(self, doc):\n",
    "            \"\"\"\n",
    "            Returns a vectorized padded version of sequences.\n",
    "            \"\"\"\n",
    "            save_path = \"./Data/padding.pickle\"\n",
    "            with open(save_path, 'rb') as f:\n",
    "                tokenizer = pickle.load(f)\n",
    "            doc_pad = tokenizer.texts_to_sequences(doc)\n",
    "            doc_pad = pad_sequences(doc_pad, padding='pre', truncating='pre', maxlen=self.max_sentence_len)\n",
    "            return np.squeeze(doc_pad)\n",
    "\n",
    "        def lemmatize(self, token, tag):\n",
    "            \"\"\"\n",
    "            Converts the Penn Treebank tag to a WordNet POS tag, then uses that\n",
    "            tag to perform WordNet lemmatization.\n",
    "            \"\"\"\n",
    "            tag = {\n",
    "                'N': wn.NOUN,\n",
    "                'V': wn.VERB,\n",
    "                'R': wn.ADV,\n",
    "                'J': wn.ADJ\n",
    "            }.get(tag[0], wn.NOUN)\n",
    "\n",
    "            return self.lemmatizer.lemmatize(token, tag)\n",
    "\n",
    "\n",
    "    class MyRNNTransformer(BaseEstimator, TransformerMixin):\n",
    "        \"\"\"\n",
    "        Transformer allowing our Keras model to be included in our pipeline\n",
    "        \"\"\"\n",
    "        def __init__(self, classifier):\n",
    "            self.classifier = classifier\n",
    "\n",
    "        def fit(self, X, y):\n",
    "            batch_size = 32\n",
    "            num_epochs = 35\n",
    "            batch_size = batch_size\n",
    "            epochs = num_epochs\n",
    "            self.classifier.fit(X, y, epochs=epochs, batch_size=batch_size, verbose=2)\n",
    "            return self\n",
    "\n",
    "        def transform(self, X):\n",
    "            self.pred = self.classifier.predict_proba(X)\n",
    "            self.classes = [[0 if el < 0.2 else 1 for el in item] for item in self.pred]\n",
    "            return self.classes\n",
    "\n",
    "\n",
    "    class TfidfEmbeddingVectorizer(object):\n",
    "        def __init__(self, word2vec):\n",
    "            self.word2vec = word2vec\n",
    "            self.word2weight = None\n",
    "            self.dim = len(word2vec.values())\n",
    "\n",
    "        def fit(self, X, y):\n",
    "            tfidf = TfidfVectorizer(analyzer=lambda x: x)\n",
    "            tfidf.fit(X)\n",
    "            # if a word was never seen - it must be at least as infrequent\n",
    "            # as any of the known words. So the default idf is the max of\n",
    "            # known idf's\n",
    "            max_idf = max(tfidf.idf_)\n",
    "            self.word2weight = defaultdict(\n",
    "                lambda: max_idf,\n",
    "                [(w, tfidf.idf_[i]) for w, i in tfidf.vocabulary_.items()])\n",
    "\n",
    "            return self\n",
    "\n",
    "        def transform(self, X):\n",
    "            return np.array([\n",
    "                np.mean([self.word2vec[w] * self.word2weight[w]\n",
    "                         for w in words if w in self.word2vec] or\n",
    "                        [np.zeros(self.dim)], axis=0)\n",
    "                for words in X\n",
    "            ])\n",
    "\n",
    "\n",
    "    def identity(self, arg):\n",
    "        \"\"\"\n",
    "        Simple identity function works as a passthrough.\n",
    "        \"\"\"\n",
    "        return arg\n",
    "\n",
    "\n",
    "    def reshape_a_feature_column(self, series):\n",
    "        return np.reshape(np.asarray(series), (len(series), 1))\n",
    "\n",
    "\n",
    "    def pipelinize_feature(self, function, active=True):\n",
    "        def list_comprehend_a_function(list_or_series, active=True):\n",
    "            if active:\n",
    "                processed = [function(i) for i in list_or_series]\n",
    "                processed = self.reshape_a_feature_column(processed)\n",
    "                return processed\n",
    "            else:\n",
    "                return self.reshape_a_feature_column(np.zeros(len(list_or_series)))\n",
    "\n",
    "\n",
    "    def get_text_length(self, text):\n",
    "        return len(text)\n",
    "\n",
    "    def load_google_vec(self):\n",
    "        #url = 'https://s3.amazonaws.com/dl4j-distribution/GoogleNews-vectors-negative300.bin.gz'\n",
    "        #wget.download(url, 'Data/GoogleNews-vectors.bin.gz')\n",
    "        return KeyedVectors.load_word2vec_format(\n",
    "            'Data/GoogleNews-vectors.bin.gz',\n",
    "            binary=True)\n",
    "\n",
    "\n",
    "    def lemmatize_token(self, token, tag):\n",
    "        tag = {\n",
    "            'N': wn.NOUN,\n",
    "            'V': wn.VERB,\n",
    "            'R': wn.ADV,\n",
    "            'J': wn.ADJ\n",
    "        }.get(tag[0], wn.NOUN)\n",
    "        return WordNetLemmatizer().lemmatize(token, tag)\n",
    "\n",
    "\n",
    "    def get_preprocessed_corpus(self, X_corpus):\n",
    "        \"\"\"\n",
    "        Returns a preprocessed version of a full corpus (ie. tokenization and lemmatization using POS taggs)\n",
    "        \"\"\"\n",
    "        X = ' '.join(X_corpus)\n",
    "        lemmatized_tokens = []\n",
    "\n",
    "        # Break the document into sentences\n",
    "        for sent in sent_tokenize(X):\n",
    "\n",
    "            # Break the sentence into part of speech tagged tokens\n",
    "            for token, tag in pos_tag(wordpunct_tokenize(sent)):\n",
    "\n",
    "                # Apply preprocessing to the token\n",
    "                token = token.lower()\n",
    "                token = token.strip()\n",
    "                token = token.strip('_')\n",
    "                token = token.strip('*')\n",
    "\n",
    "                # If punctuation or stopword, ignore token and continue\n",
    "                if token in set(sw.words('english')) or all(char in set(string.punctuation) for char in token):\n",
    "                    continue\n",
    "\n",
    "                # Lemmatize the token and yield\n",
    "                lemma = self.lemmatize_token(token, tag)\n",
    "                lemmatized_tokens.append(lemma)\n",
    "\n",
    "        doc = ' '.join(lemmatized_tokens)\n",
    "        return doc\n",
    "\n",
    "\n",
    "    def prepare_embedding(self, X):\n",
    "        \"\"\"\n",
    "        Returns the embedding weights matrix, the word index, and the word-vector dictionnary corresponding\n",
    "        to the training corpus set of words.\n",
    "        \"\"\"\n",
    "        # Load Word2Vec vectors\n",
    "        word2vec = self.load_google_vec()\n",
    "\n",
    "        # Fit and apply an NLTK tokenizer on the preprocessed training corpus to obtain sequences.\n",
    "        tokenizer = Tokenizer(num_words=self.max_features)\n",
    "        X_pad = self.get_preprocessed_corpus(X)\n",
    "        tokenizer.fit_on_texts(pd.Series(X_pad))\n",
    "        X_pad = tokenizer.texts_to_sequences(pd.Series(X_pad))\n",
    "\n",
    "        # Pad the sequences\n",
    "        X_pad = pad_sequences(X_pad, maxlen=self.max_sentence_len, padding='post', truncating='post')\n",
    "\n",
    "        # Retrieve the word index\n",
    "        train_word_index = tokenizer.word_index\n",
    "\n",
    "        # Construct the embedding weights matrix and word-vector dictionnary\n",
    "        train_embedding_weights = np.zeros((len(train_word_index) + 1, self.embed_dim))\n",
    "        for word, index in train_word_index.items():\n",
    "            train_embedding_weights[index, :] = word2vec[word] if word in word2vec else np.random.rand(self.embed_dim)\n",
    "        word_vector_dict = dict(zip(pd.Series(list(train_word_index.keys())),\n",
    "                                    pd.Series(list(train_word_index.keys())).apply(\n",
    "                                        lambda x: train_embedding_weights[train_word_index[x]])))\n",
    "        return train_embedding_weights, train_word_index, word_vector_dict\n",
    "\n",
    "\n",
    "\n",
    "    def multiclass_accuracy(self,predictions, target):\n",
    "        \"Returns the multiclass accuracy of the classifier's predictions\"\n",
    "        score = []\n",
    "        for j in range(0, 5):\n",
    "            count = 0\n",
    "            for i in range(len(predictions)):\n",
    "                if predictions[i][j] == target[i][j]:\n",
    "                    count += 1\n",
    "            score.append(count / len(predictions))\n",
    "        return score\n",
    "\n",
    "\n",
    "    def run(self,X, y, classifier=SGDClassifier, model_name=None,\n",
    "                           verbose=True):\n",
    "        \"\"\"\n",
    "        Builds a classifer for the given list of documents and targets\n",
    "        \"\"\"\n",
    "        def build(classifier, X, y, embedding_dict, corpus):\n",
    "            \"\"\"\n",
    "            Inner build function that builds a single model.\n",
    "            \"\"\"\n",
    "            classifier = OneVsRestClassifier(classifier(loss = \"modified_huber\", alpha=0.00000001), n_jobs=-1)\n",
    "            model = Pipeline([\n",
    "                ('preprocessor', self.NLTKPreprocessor),\n",
    "                (\"wordVectz\", self.TfidfEmbeddingVectorizer(embedding_dict)),\n",
    "                ('clf', classifier)\n",
    "            ])\n",
    "            return model.fit(X, y)\n",
    "\n",
    "        y_trans = y\n",
    "\n",
    "        # Prepare the embedding\n",
    "        train_embedding_weights, train_word_index, wv_dict = self.prepare_embedding(X)\n",
    "\n",
    "        # Begin evaluation\n",
    "        if verbose: print(\"Building complete model and saving ...\")\n",
    "        model = build(classifier, X, y_trans, wv_dict, corpus=X)\n",
    "        \n",
    "        # Save the model\n",
    "        if model_name:\n",
    "            outpath = 'Models/'\n",
    "            with open(outpath + model_name, 'wb') as f:\n",
    "                dill.dump(model, f)\n",
    "            print(\"Model written out to {}\".format(model_name))\n",
    "\n",
    "        return model\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building complete model and saving ...\n",
      "Model written out to Personality_traits_SVM\n"
     ]
    }
   ],
   "source": [
    "model = train_svm(X_train).run(X_train, y_train, model_name = \"Personality_traits_SVM\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Test models"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Test Keras model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "class test:\n",
    "\n",
    "    def __init__(self):\n",
    "        self.max_sentence_len = 300\n",
    "        self.max_features = 300\n",
    "        self.embed_dim = 300\n",
    "        self.NLTKPreprocessor = self.NLTKPreprocessor()\n",
    "        #self.MyRNNTransformer = self.MyRNNTransformer()\n",
    "\n",
    "\n",
    "    class NLTKPreprocessor(BaseEstimator, TransformerMixin):\n",
    "        \"\"\"\n",
    "        Transforms input data by using NLTK tokenization, POS tagging, lemmatization and vectorization.\n",
    "        \"\"\"\n",
    "\n",
    "        def __init__(self, max_sentence_len = 300, stopwords=None, punct=None, lower=True, strip=True):\n",
    "            \"\"\"\n",
    "            Instantiates the preprocessor.\n",
    "            \"\"\"\n",
    "            self.lower = lower\n",
    "            self.strip = strip\n",
    "            self.stopwords = set(stopwords) if stopwords else set(sw.words('english'))\n",
    "            self.punct = set(punct) if punct else set(string.punctuation)\n",
    "            self.lemmatizer = WordNetLemmatizer()\n",
    "            self.max_sentence_len = max_sentence_len\n",
    "\n",
    "        def fit(self, X, y=None):\n",
    "            \"\"\"\n",
    "            Fit simply returns self.\n",
    "            \"\"\"\n",
    "            return self\n",
    "\n",
    "        def inverse_transform(self, X):\n",
    "            \"\"\"\n",
    "            No inverse transformation.\n",
    "            \"\"\"\n",
    "            return X\n",
    "\n",
    "        def transform(self, X):\n",
    "            \"\"\"\n",
    "            Actually runs the preprocessing on each document.\n",
    "            \"\"\"\n",
    "            output = np.array([(self.tokenize(doc)) for doc in X])\n",
    "            return output\n",
    "\n",
    "        def tokenize(self, document):\n",
    "            \"\"\"\n",
    "            Returns a normalized, lemmatized list of tokens from a document by\n",
    "            applying segmentation, tokenization, and part of speech tagging.\n",
    "            Uses the part of speech tags to look up the lemma in WordNet, and returns the lowercase\n",
    "            version of all the words, removing stopwords and punctuation.\n",
    "            \"\"\"\n",
    "            lemmatized_tokens = []\n",
    "\n",
    "            # Clean the text\n",
    "            document = re.sub(r\"[^A-Za-z0-9^,!.\\/'+-=]\", \" \", document)\n",
    "            document = re.sub(r\"what's\", \"what is \", document)\n",
    "            document = re.sub(r\"\\'s\", \" \", document)\n",
    "            document = re.sub(r\"\\'ve\", \" have \", document)\n",
    "            document = re.sub(r\"can't\", \"cannot \", document)\n",
    "            document = re.sub(r\"n't\", \" not \", document)\n",
    "            document = re.sub(r\"i'm\", \"i am \", document)\n",
    "            document = re.sub(r\"\\'re\", \" are \", document)\n",
    "            document = re.sub(r\"\\'d\", \" would \", document)\n",
    "            document = re.sub(r\"\\'ll\", \" will \", document)\n",
    "            document = re.sub(r\"(\\d+)(k)\", r\"\\g<1>000\", document)\n",
    "\n",
    "            # Break the document into sentences\n",
    "            for sent in sent_tokenize(document):\n",
    "\n",
    "                # Break the sentence into part of speech tagged tokens\n",
    "                for token, tag in pos_tag(wordpunct_tokenize(sent)):\n",
    "\n",
    "                    # Apply preprocessing to the token\n",
    "                    token = token.lower() if self.lower else token\n",
    "                    token = token.strip() if self.strip else token\n",
    "                    token = token.strip('_') if self.strip else token\n",
    "                    token = token.strip('*') if self.strip else token\n",
    "\n",
    "                    # If punctuation or stopword, ignore token and continue\n",
    "                    if token in self.stopwords or all(char in self.punct for char in token):\n",
    "                        continue\n",
    "\n",
    "                    # Lemmatize the token\n",
    "                    lemma = self.lemmatize(token, tag)\n",
    "                    lemmatized_tokens.append(lemma)\n",
    "\n",
    "            doc = ' '.join(lemmatized_tokens)\n",
    "            tokenized_document = self.vectorize(np.array(doc)[np.newaxis])\n",
    "            return tokenized_document\n",
    "\n",
    "\n",
    "        def vectorize(self, doc):\n",
    "            \"\"\"\n",
    "            Returns a vectorized padded version of sequences.\n",
    "            \"\"\"\n",
    "            save_path = \"./Data/padding.pickle\"\n",
    "            with open(save_path, 'rb') as f:\n",
    "                tokenizer = pickle.load(f)\n",
    "            doc_pad = tokenizer.texts_to_sequences(doc)\n",
    "            doc_pad = pad_sequences(doc_pad, padding='pre', truncating='pre', maxlen=self.max_sentence_len)\n",
    "            return np.squeeze(doc_pad)\n",
    "\n",
    "        def lemmatize(self, token, tag):\n",
    "            \"\"\"\n",
    "            Converts the Penn Treebank tag to a WordNet POS tag, then uses that\n",
    "            tag to perform WordNet lemmatization.\n",
    "            \"\"\"\n",
    "            tag = {\n",
    "                'N': wn.NOUN,\n",
    "                'V': wn.VERB,\n",
    "                'R': wn.ADV,\n",
    "                'J': wn.ADJ\n",
    "            }.get(tag[0], wn.NOUN)\n",
    "\n",
    "            return self.lemmatizer.lemmatize(token, tag)\n",
    "\n",
    "\n",
    "    class MyRNNTransformer(BaseEstimator, TransformerMixin):\n",
    "        \"\"\"\n",
    "        Transformer allowing our Keras model to be included in our pipeline\n",
    "        \"\"\"\n",
    "        def __init__(self, classifier):\n",
    "            self.classifier = classifier\n",
    "\n",
    "        def fit(self, X, y):\n",
    "            batch_size = 32\n",
    "            num_epochs = 35\n",
    "            batch_size = batch_size\n",
    "            epochs = num_epochs\n",
    "            self.classifier.fit(X, y, epochs=epochs, batch_size=batch_size, verbose=2)\n",
    "            return self\n",
    "\n",
    "        def transform(self, X):\n",
    "            self.pred = self.classifier.predict(X)\n",
    "            self.classes = [[0 if el < 0.2 else 1 for el in item] for item in self.pred]\n",
    "            return self.pred\n",
    "\n",
    "    def multiclass_accuracy(self,predictions, target):\n",
    "        \"Returns the multiclass accuracy of the classifier's predictions\"\n",
    "        score = []\n",
    "        for j in range(0, 5):\n",
    "            count = 0\n",
    "            for i in range(len(predictions)):\n",
    "                if predictions[i][j] == target[i][j]:\n",
    "                    count += 1\n",
    "            score.append(count / len(predictions))\n",
    "        return score\n",
    "\n",
    "\n",
    "    def run(self, X, y, model_name):\n",
    "        \"\"\"\n",
    "        Returns the predictions from the pipeline including our NLTKPreprocessor and Keras classifier.\n",
    "        \"\"\"\n",
    "        def build(classifier):\n",
    "            \"\"\"\n",
    "            Inner build function that builds a pipeline including a preprocessor and a classifier.\n",
    "            \"\"\"\n",
    "            model = Pipeline([\n",
    "                    ('preprocessor', self.NLTKPreprocessor),\n",
    "                    ('classifier', classifier)\n",
    "                ])\n",
    "            return model\n",
    "\n",
    "        save_path = './Models/'\n",
    "        json_file = open(save_path + model_name + '.json', 'r')\n",
    "        classifier = model_from_json(json_file.read())\n",
    "        classifier.load_weights(save_path + model_name + '.h5')\n",
    "        classifier.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])\n",
    "        json_file.close()\n",
    "        model = build(self.MyRNNTransformer(classifier))\n",
    "        y_pred = model.transform(X)\n",
    "        y_pred_classes = [[0 if el < 0.2 else 1 for el in item] for item in y_pred]\n",
    "        print(self.multiclass_accuracy(y.values.tolist(), y_pred_classes))\n",
    "\n",
    "        return y_pred"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0.6437246963562753, 0.771255060728745, 0.7125506072874493, 0.8117408906882592, 0.8502024291497976]\n"
     ]
    }
   ],
   "source": [
    "pred = test().run(X_test, y_test, model_name = \"Personality_traits_NN\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Test SVM"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "metadata": {},
   "outputs": [],
   "source": [
    "class test_svm:\n",
    "\n",
    "    def __init__(self):\n",
    "        self.max_sentence_len = 300\n",
    "        self.NLTKPreprocessor = self.NLTKPreprocessor()\n",
    "        #self.MyRNNTransformer = self.MyRNNTransformer()\n",
    "\n",
    "\n",
    "    class NLTKPreprocessor(BaseEstimator, TransformerMixin):\n",
    "        \"\"\"\n",
    "        Transforms input data by using NLTK tokenization, POS tagging, lemmatization and vectorization.\n",
    "        \"\"\"\n",
    "\n",
    "        def __init__(self, max_sentence_len = 300, stopwords=None, punct=None, lower=True, strip=True):\n",
    "            \"\"\"\n",
    "            Instantiates the preprocessor.\n",
    "            \"\"\"\n",
    "            self.lower = lower\n",
    "            self.strip = strip\n",
    "            self.stopwords = set(stopwords) if stopwords else set(sw.words('english'))\n",
    "            self.punct = set(punct) if punct else set(string.punctuation)\n",
    "            self.lemmatizer = WordNetLemmatizer()\n",
    "            self.max_sentence_len = max_sentence_len\n",
    "\n",
    "        def fit(self, X, y=None):\n",
    "            \"\"\"\n",
    "            Fit simply returns self.\n",
    "            \"\"\"\n",
    "            return self\n",
    "\n",
    "        def inverse_transform(self, X):\n",
    "            \"\"\"\n",
    "            No inverse transformation.\n",
    "            \"\"\"\n",
    "            return X\n",
    "\n",
    "        def transform(self, X):\n",
    "            \"\"\"\n",
    "            Actually runs the preprocessing on each document.\n",
    "            \"\"\"\n",
    "            output = np.array([(self.tokenize(doc)) for doc in X])\n",
    "            return output\n",
    "\n",
    "        def tokenize(self, document):\n",
    "            \"\"\"\n",
    "            Returns a normalized, lemmatized list of tokens from a document by\n",
    "            applying segmentation, tokenization, and part of speech tagging.\n",
    "            Uses the part of speech tags to look up the lemma in WordNet, and returns the lowercase\n",
    "            version of all the words, removing stopwords and punctuation.\n",
    "            \"\"\"\n",
    "            lemmatized_tokens = []\n",
    "\n",
    "            # Clean the text\n",
    "            document = re.sub(r\"[^A-Za-z0-9^,!.\\/'+-=]\", \" \", document)\n",
    "            document = re.sub(r\"what's\", \"what is \", document)\n",
    "            document = re.sub(r\"\\'s\", \" \", document)\n",
    "            document = re.sub(r\"\\'ve\", \" have \", document)\n",
    "            document = re.sub(r\"can't\", \"cannot \", document)\n",
    "            document = re.sub(r\"n't\", \" not \", document)\n",
    "            document = re.sub(r\"i'm\", \"i am \", document)\n",
    "            document = re.sub(r\"\\'re\", \" are \", document)\n",
    "            document = re.sub(r\"\\'d\", \" would \", document)\n",
    "            document = re.sub(r\"\\'ll\", \" will \", document)\n",
    "            document = re.sub(r\"(\\d+)(k)\", r\"\\g<1>000\", document)\n",
    "\n",
    "            # Break the document into sentences\n",
    "            for sent in sent_tokenize(document):\n",
    "\n",
    "                # Break the sentence into part of speech tagged tokens\n",
    "                for token, tag in pos_tag(wordpunct_tokenize(sent)):\n",
    "\n",
    "                    # Apply preprocessing to the token\n",
    "                    token = token.lower() if self.lower else token\n",
    "                    token = token.strip() if self.strip else token\n",
    "                    token = token.strip('_') if self.strip else token\n",
    "                    token = token.strip('*') if self.strip else token\n",
    "\n",
    "                    # If punctuation or stopword, ignore token and continue\n",
    "                    if token in self.stopwords or all(char in self.punct for char in token):\n",
    "                        continue\n",
    "\n",
    "                    # Lemmatize the token\n",
    "                    lemma = self.lemmatize(token, tag)\n",
    "                    lemmatized_tokens.append(lemma)\n",
    "\n",
    "            doc = ' '.join(lemmatized_tokens)\n",
    "            tokenized_document = self.vectorize(np.array(doc)[np.newaxis])\n",
    "            return tokenized_document\n",
    "\n",
    "\n",
    "        def vectorize(self, doc):\n",
    "            \"\"\"\n",
    "            Returns a vectorized padded version of sequences.\n",
    "            \"\"\"\n",
    "            save_path = \"./Data/padding.pickle\"\n",
    "            with open(save_path, 'rb') as f:\n",
    "                tokenizer = pickle.load(f)\n",
    "            doc_pad = tokenizer.texts_to_sequences(doc)\n",
    "            doc_pad = pad_sequences(doc_pad, padding='pre', truncating='pre', maxlen=self.max_sentence_len)\n",
    "            return np.squeeze(doc_pad)\n",
    "\n",
    "        def lemmatize(self, token, tag):\n",
    "            \"\"\"\n",
    "            Converts the Penn Treebank tag to a WordNet POS tag, then uses that\n",
    "            tag to perform WordNet lemmatization.\n",
    "            \"\"\"\n",
    "            tag = {\n",
    "                'N': wn.NOUN,\n",
    "                'V': wn.VERB,\n",
    "                'R': wn.ADV,\n",
    "                'J': wn.ADJ\n",
    "            }.get(tag[0], wn.NOUN)\n",
    "\n",
    "            return self.lemmatizer.lemmatize(token, tag)\n",
    "\n",
    "\n",
    "    class MyRNNTransformer(BaseEstimator, TransformerMixin):\n",
    "        \"\"\"\n",
    "        Transformer allowing our Keras model to be included in our pipeline\n",
    "        \"\"\"\n",
    "        def __init__(self, classifier):\n",
    "            self.classifier = classifier\n",
    "\n",
    "        def fit(self, X, y):\n",
    "            batch_size = 32\n",
    "            num_epochs = 35\n",
    "            batch_size = batch_size\n",
    "            epochs = num_epochs\n",
    "            self.classifier.fit(X, y, epochs=epochs, batch_size=batch_size, verbose=2)\n",
    "            return self\n",
    "\n",
    "        def transform(self, X):\n",
    "            self.pred = self.classifier.predict_proba(X)\n",
    "            self.classes = [[0 if el < 0.2 else 1 for el in item] for item in self.pred]\n",
    "            return self.pred\n",
    "\n",
    "\n",
    "    def identity(self, arg):\n",
    "        \"\"\"\n",
    "        Simple identity function works as a passthrough.\n",
    "        \"\"\"\n",
    "        return arg\n",
    "\n",
    "\n",
    "    def reshape_a_feature_column(self, series):\n",
    "        return np.reshape(np.asarray(series), (len(series), 1))\n",
    "\n",
    "\n",
    "    def pipelinize_feature(self, function, active=True):\n",
    "        def list_comprehend_a_function(list_or_series, active=True):\n",
    "            if active:\n",
    "                processed = [function(i) for i in list_or_series]\n",
    "                processed = self.reshape_a_feature_column(processed)\n",
    "                return processed\n",
    "            else:\n",
    "                return self.reshape_a_feature_column(np.zeros(len(list_or_series)))\n",
    "\n",
    "\n",
    "    def get_text_length(self, text):\n",
    "        return len(text)\n",
    "\n",
    "\n",
    "    def multiclass_accuracy(self,predictions, target):\n",
    "        \"Returns the multiclass accuracy of the classifier's predictions\"\n",
    "        score = []\n",
    "        for j in range(0, 5):\n",
    "            count = 0\n",
    "            for i in range(len(predictions)):\n",
    "                if predictions[i][j] == target[i][j]:\n",
    "                    count += 1\n",
    "            score.append(count / len(predictions))\n",
    "        return score\n",
    "\n",
    "\n",
    "    def run(self, X, y, model_name):\n",
    "        \"\"\"\n",
    "        Returns the predictions from the pipeline including our NLTKPreprocessor and SVM classifier.\n",
    "        \"\"\"\n",
    "        save_path = \"./Models/\"\n",
    "        with open(save_path + model_name, 'rb') as f:\n",
    "            model = dill.load(f)\n",
    "        y_pred = model.predict_proba(X)\n",
    "        y_pred_classes = [[0 if el < 0.2 else 1 for el in item] for item in y_pred]\n",
    "        print(self.multiclass_accuracy(y.values.tolist(), y_pred_classes))\n",
    "\n",
    "        return y_pred"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0.5121457489878543, 0.5020242914979757, 0.5465587044534413, 0.5040485829959515, 0.4959514170040486]\n"
     ]
    }
   ],
   "source": [
    "pred = test_svm().run(X_test, y_test, \"Personality_traits_SVM\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Predict single outputs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Predict with Keras model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "class predict:\n",
    "    \n",
    "    def __init__(self):\n",
    "        self.max_sentence_len = 300\n",
    "        self.max_features = 300\n",
    "        self.embed_dim = 300\n",
    "        self.NLTKPreprocessor = self.NLTKPreprocessor()\n",
    "\n",
    "\n",
    "    class NLTKPreprocessor(BaseEstimator, TransformerMixin):\n",
    "        \"\"\"\n",
    "        Transforms input data by using NLTK tokenization, POS tagging, lemmatization and vectorization.\n",
    "        \"\"\"\n",
    "\n",
    "        def __init__(self, max_sentence_len = 300, stopwords=None, punct=None, lower=True, strip=True):\n",
    "            \"\"\"\n",
    "            Instantiates the preprocessor.\n",
    "            \"\"\"\n",
    "            self.lower = lower\n",
    "            self.strip = strip\n",
    "            self.stopwords = set(stopwords) if stopwords else set(sw.words('english'))\n",
    "            self.punct = set(punct) if punct else set(string.punctuation)\n",
    "            self.lemmatizer = WordNetLemmatizer()\n",
    "            self.max_sentence_len = max_sentence_len\n",
    "\n",
    "        def fit(self, X, y=None):\n",
    "            \"\"\"\n",
    "            Fit simply returns self.\n",
    "            \"\"\"\n",
    "            return self\n",
    "\n",
    "        def inverse_transform(self, X):\n",
    "            \"\"\"\n",
    "            No inverse transformation.\n",
    "            \"\"\"\n",
    "            return X\n",
    "\n",
    "        def transform(self, X):\n",
    "            \"\"\"\n",
    "            Actually runs the preprocessing on each document.\n",
    "            \"\"\"\n",
    "            output = np.array([(self.tokenize(doc)) for doc in X])\n",
    "            return output\n",
    "\n",
    "        def tokenize(self, document):\n",
    "            \"\"\"\n",
    "            Returns a normalized, lemmatized list of tokens from a document by\n",
    "            applying segmentation, tokenization, and part of speech tagging.\n",
    "            Uses the part of speech tags to look up the lemma in WordNet, and returns the lowercase\n",
    "            version of all the words, removing stopwords and punctuation.\n",
    "            \"\"\"\n",
    "            lemmatized_tokens = []\n",
    "\n",
    "            # Clean the text\n",
    "            document = re.sub(r\"[^A-Za-z0-9^,!.\\/'+-=]\", \" \", document)\n",
    "            document = re.sub(r\"what's\", \"what is \", document)\n",
    "            document = re.sub(r\"\\'s\", \" \", document)\n",
    "            document = re.sub(r\"\\'ve\", \" have \", document)\n",
    "            document = re.sub(r\"can't\", \"cannot \", document)\n",
    "            document = re.sub(r\"n't\", \" not \", document)\n",
    "            document = re.sub(r\"i'm\", \"i am \", document)\n",
    "            document = re.sub(r\"\\'re\", \" are \", document)\n",
    "            document = re.sub(r\"\\'d\", \" would \", document)\n",
    "            document = re.sub(r\"\\'ll\", \" will \", document)\n",
    "            document = re.sub(r\"(\\d+)(k)\", r\"\\g<1>000\", document)\n",
    "\n",
    "            # Break the document into sentences\n",
    "            for sent in sent_tokenize(document):\n",
    "\n",
    "                # Break the sentence into part of speech tagged tokens\n",
    "                for token, tag in pos_tag(wordpunct_tokenize(sent)):\n",
    "\n",
    "                    # Apply preprocessing to the token\n",
    "                    token = token.lower() if self.lower else token\n",
    "                    token = token.strip() if self.strip else token\n",
    "                    token = token.strip('_') if self.strip else token\n",
    "                    token = token.strip('*') if self.strip else token\n",
    "\n",
    "                    # If punctuation or stopword, ignore token and continue\n",
    "                    if token in self.stopwords or all(char in self.punct for char in token):\n",
    "                        continue\n",
    "\n",
    "                    # Lemmatize the token\n",
    "                    lemma = self.lemmatize(token, tag)\n",
    "                    lemmatized_tokens.append(lemma)\n",
    "\n",
    "            doc = ' '.join(lemmatized_tokens)\n",
    "            tokenized_document = self.vectorize(np.array(doc)[np.newaxis])\n",
    "            return tokenized_document\n",
    "\n",
    "\n",
    "        def vectorize(self, doc):\n",
    "            \"\"\"\n",
    "            Returns a vectorized padded version of sequences.\n",
    "            \"\"\"\n",
    "            save_path = \"./Data/padding.pickle\"\n",
    "            with open(save_path, 'rb') as f:\n",
    "                tokenizer = pickle.load(f)\n",
    "            doc_pad = tokenizer.texts_to_sequences(doc)\n",
    "            doc_pad = pad_sequences(doc_pad, padding='pre', truncating='pre', maxlen=self.max_sentence_len)\n",
    "            return np.squeeze(doc_pad)\n",
    "\n",
    "        def lemmatize(self, token, tag):\n",
    "            \"\"\"\n",
    "            Converts the Penn Treebank tag to a WordNet POS tag, then uses that\n",
    "            tag to perform WordNet lemmatization.\n",
    "            \"\"\"\n",
    "            tag = {\n",
    "                'N': wn.NOUN,\n",
    "                'V': wn.VERB,\n",
    "                'R': wn.ADV,\n",
    "                'J': wn.ADJ\n",
    "            }.get(tag[0], wn.NOUN)\n",
    "\n",
    "            return self.lemmatizer.lemmatize(token, tag)\n",
    "\n",
    "\n",
    "    class MyRNNTransformer(BaseEstimator, TransformerMixin):\n",
    "        \"\"\"\n",
    "        Transformer allowing our Keras model to be included in our pipeline\n",
    "        \"\"\"\n",
    "        def __init__(self, classifier):\n",
    "            self.classifier = classifier\n",
    "\n",
    "        def fit(self, X, y):\n",
    "            batch_size = 32\n",
    "            num_epochs = 35\n",
    "            batch_size = batch_size\n",
    "            epochs = num_epochs\n",
    "            self.classifier.fit(X, y, epochs=epochs, batch_size=batch_size, verbose=2)\n",
    "            return self\n",
    "\n",
    "        def transform(self, X):\n",
    "            self.pred = self.classifier.predict(X)\n",
    "            self.classes = [[0 if el < 0.2 else 1 for el in item] for item in self.pred]\n",
    "            return self.pred\n",
    "\n",
    "\n",
    "    def run(self, X, model_name):\n",
    "        \"\"\"\n",
    "        Returns the predictions from the pipeline including our NLTKPreprocessor and Keras classifier.\n",
    "        \"\"\"\n",
    "        def build(classifier):\n",
    "            \"\"\"\n",
    "            Inner build function that builds a pipeline including a preprocessor and a classifier.\n",
    "            \"\"\"\n",
    "            model = Pipeline([\n",
    "                    ('preprocessor', self.NLTKPreprocessor),\n",
    "                    ('classifier', classifier)\n",
    "                ])\n",
    "            return model\n",
    "\n",
    "        save_path = './Models/'\n",
    "        json_file = open(save_path + model_name + '.json', 'r')\n",
    "        classifier = model_from_json(json_file.read())\n",
    "        classifier.load_weights(save_path + model_name + '.h5')\n",
    "        classifier.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])\n",
    "        json_file.close()\n",
    "        model = build(self.MyRNNTransformer(classifier))\n",
    "        y_pred = model.transform([X])\n",
    "        \n",
    "        return y_pred"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['I hate this type of assignment, the kind of work that tries to force a thought into one\\'s head. Thoughts should come naturally, and when the mind is prompted, then nothing comes to it. Well, as long as I\\'m supposed to be thinking, let\\'s think about why I\\'m doing this. It\\'s to get a good grade. Why do I do anything. I always seem to respond in a manner in which the answer seems acceptable to everyone else. Maybe I\\'m doing this to please everyone else: my instructor, my parents, myself. So why do I do everything else that I do?  Why do I devote so much time to working out and exercise?  Is it because I want to fool myself into living longer?  Is it to mold my body in such a way as to attract others and get their attentions. I always say that I just don\\'t want to become weak, that when I find someone to protect, I want to be able to protect them. But if I am always desparately trying to find this person that would fill the current void in my life, why is it I push everyone away?  My friends, my peers, my elders?  I don\\'t do what everyone says is \"fun. \"  I don\\'t hang around others for long, I don\\'t go to parties, and I rarely impart what I truly feel. It seems as if I\\'ve been hiding half my life, hiding from others and from myself. Inside, I scream to myself, \"go out have fun meet new people you boring dolt!\"  and in my mind I\\'m always prepared to do so, but when the moment of truth comes about, I never act out my true intentions, either being intimidated or discouraged. I remember when I went to that UT freshman orientation. My friend said when you get to the dance, just go up to people, introduce yourself, and ask for a dance. And he\\'s a loser. But it worked for him, so I tried it myself. The first time, rejected. The second time, rejected. The third time, rejected, and even I know that 3 strikes is out. I get so sick of hearing excuses like oh I\\'m too tired or I don\\'t feel like it. Yeah right. Then why the hell are you here at the dance?  Just give me a straight and vehement no and I\\'ll be on my way, not even to think twice about the encounter. Now on strike three, I had another reason for retiring that night. That girl, the way she looked, the way she acted, it was deja vu. I went to another country with a girl like that, stood by her side, opened my heart to her, and offered it to her. She led me on, to believe that for once, I had an opportunity for love, that it was my time in the spotlight, and with the most beautiful girl I have ever been with. Visually stunning, patient but forceful, and gentle, someone to listen. I thought I finally found her, and felt as though I was riding above clouds. Then I just came down hard with the rain. I\\'ve never been the same afterwards, because everything was seen in a different light. This third girl at the dance, the rudeness didn\\'t get to me, but the memories of heartbreak was just too much to handle. I asked Tonia out to the dance once, and she came. But we never danced. I asked and she ignored. Just like This time. Love. I\\'m too young to know its true meanings, and too inexperienced to have any justification in even corrolating it with the words I or me. I\\'m always looking for \"love\" for companionship without even really knowing what to look for. It can\\'t just be the person that gives you that indescribable burning feeling within. Too many times has that happened. I say to myself, you know what love is. You loved Tonia, because all you ever did was argue, yet you could stand it. She broke your heart, lied to you, worked behind your back, carried you up and dropped you, and yet you still thought of her. Hell, you still think of her. You\\'ve been talking to her as a friend and the feeling has never left you. Isn\\'t that enough to be love?  It\\'s as close as I\\'ve gotten, I\\'m sure, but what about prom. To have danced with someone I\\'ve never known before, to talk, and to, after so long, have fun. When I held her in my arms, and her hair brushed my face as the slow songs played through, what was that feeling. Was it a feeling of completeness, of safety that as long as with her that nothing can go wrong?  What was that?  I go through sleepless nights thinking of Christie sometimes, someone who I never gave a second thought to until that moment I told her goodbye. The moment in which the swelling in my throat caught even me by surprise. But am I looking for love or just someone. I don\\'t want to end up like my uncle, having married a woman only because she threatened to commit suicide. Is he happy?  Am I happy now?  no, I\\'m not. ']\n"
     ]
    }
   ],
   "source": [
    "pred = predict().run(X_test[0], model_name = \"Personality_traits_NN\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Predict with SVM"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [],
   "source": [
    "class predict_svm:\n",
    "\n",
    "    def __init__(self):\n",
    "        self.max_sentence_len = 300\n",
    "        self.NLTKPreprocessor = self.NLTKPreprocessor()\n",
    "        #self.MyRNNTransformer = self.MyRNNTransformer()\n",
    "\n",
    "\n",
    "    class NLTKPreprocessor(BaseEstimator, TransformerMixin):\n",
    "        \"\"\"\n",
    "        Transforms input data by using NLTK tokenization, POS tagging, lemmatization and vectorization.\n",
    "        \"\"\"\n",
    "\n",
    "        def __init__(self, max_sentence_len = 300, stopwords=None, punct=None, lower=True, strip=True):\n",
    "            \"\"\"\n",
    "            Instantiates the preprocessor.\n",
    "            \"\"\"\n",
    "            self.lower = lower\n",
    "            self.strip = strip\n",
    "            self.stopwords = set(stopwords) if stopwords else set(sw.words('english'))\n",
    "            self.punct = set(punct) if punct else set(string.punctuation)\n",
    "            self.lemmatizer = WordNetLemmatizer()\n",
    "            self.max_sentence_len = max_sentence_len\n",
    "\n",
    "        def fit(self, X, y=None):\n",
    "            \"\"\"\n",
    "            Fit simply returns self.\n",
    "            \"\"\"\n",
    "            return self\n",
    "\n",
    "        def inverse_transform(self, X):\n",
    "            \"\"\"\n",
    "            No inverse transformation.\n",
    "            \"\"\"\n",
    "            return X\n",
    "\n",
    "        def transform(self, X):\n",
    "            \"\"\"\n",
    "            Actually runs the preprocessing on each document.\n",
    "            \"\"\"\n",
    "            output = np.array([(self.tokenize(doc)) for doc in X])\n",
    "            return output\n",
    "\n",
    "        def tokenize(self, document):\n",
    "            \"\"\"\n",
    "            Returns a normalized, lemmatized list of tokens from a document by\n",
    "            applying segmentation, tokenization, and part of speech tagging.\n",
    "            Uses the part of speech tags to look up the lemma in WordNet, and returns the lowercase\n",
    "            version of all the words, removing stopwords and punctuation.\n",
    "            \"\"\"\n",
    "            lemmatized_tokens = []\n",
    "\n",
    "            # Clean the text\n",
    "            document = re.sub(r\"[^A-Za-z0-9^,!.\\/'+-=]\", \" \", document)\n",
    "            document = re.sub(r\"what's\", \"what is \", document)\n",
    "            document = re.sub(r\"\\'s\", \" \", document)\n",
    "            document = re.sub(r\"\\'ve\", \" have \", document)\n",
    "            document = re.sub(r\"can't\", \"cannot \", document)\n",
    "            document = re.sub(r\"n't\", \" not \", document)\n",
    "            document = re.sub(r\"i'm\", \"i am \", document)\n",
    "            document = re.sub(r\"\\'re\", \" are \", document)\n",
    "            document = re.sub(r\"\\'d\", \" would \", document)\n",
    "            document = re.sub(r\"\\'ll\", \" will \", document)\n",
    "            document = re.sub(r\"(\\d+)(k)\", r\"\\g<1>000\", document)\n",
    "\n",
    "            # Break the document into sentences\n",
    "            for sent in sent_tokenize(document):\n",
    "\n",
    "                # Break the sentence into part of speech tagged tokens\n",
    "                for token, tag in pos_tag(wordpunct_tokenize(sent)):\n",
    "\n",
    "                    # Apply preprocessing to the token\n",
    "                    token = token.lower() if self.lower else token\n",
    "                    token = token.strip() if self.strip else token\n",
    "                    token = token.strip('_') if self.strip else token\n",
    "                    token = token.strip('*') if self.strip else token\n",
    "\n",
    "                    # If punctuation or stopword, ignore token and continue\n",
    "                    if token in self.stopwords or all(char in self.punct for char in token):\n",
    "                        continue\n",
    "\n",
    "                    # Lemmatize the token\n",
    "                    lemma = self.lemmatize(token, tag)\n",
    "                    lemmatized_tokens.append(lemma)\n",
    "\n",
    "            doc = ' '.join(lemmatized_tokens)\n",
    "            tokenized_document = self.vectorize(np.array(doc)[np.newaxis])\n",
    "            return tokenized_document\n",
    "\n",
    "\n",
    "        def vectorize(self, doc):\n",
    "            \"\"\"\n",
    "            Returns a vectorized padded version of sequences.\n",
    "            \"\"\"\n",
    "            save_path = \"./Data/padding.pickle\"\n",
    "            with open(save_path, 'rb') as f:\n",
    "                tokenizer = pickle.load(f)\n",
    "            doc_pad = tokenizer.texts_to_sequences(doc)\n",
    "            doc_pad = pad_sequences(doc_pad, padding='pre', truncating='pre', maxlen=self.max_sentence_len)\n",
    "            return np.squeeze(doc_pad)\n",
    "\n",
    "        def lemmatize(self, token, tag):\n",
    "            \"\"\"\n",
    "            Converts the Penn Treebank tag to a WordNet POS tag, then uses that\n",
    "            tag to perform WordNet lemmatization.\n",
    "            \"\"\"\n",
    "            tag = {\n",
    "                'N': wn.NOUN,\n",
    "                'V': wn.VERB,\n",
    "                'R': wn.ADV,\n",
    "                'J': wn.ADJ\n",
    "            }.get(tag[0], wn.NOUN)\n",
    "\n",
    "            return self.lemmatizer.lemmatize(token, tag)\n",
    "\n",
    "\n",
    "    class MyRNNTransformer(BaseEstimator, TransformerMixin):\n",
    "        \"\"\"\n",
    "        Transformer allowing our Keras model to be included in our pipeline\n",
    "        \"\"\"\n",
    "        def __init__(self, classifier):\n",
    "            self.classifier = classifier\n",
    "\n",
    "        def fit(self, X, y):\n",
    "            batch_size = 32\n",
    "            num_epochs = 35\n",
    "            batch_size = batch_size\n",
    "            epochs = num_epochs\n",
    "            self.classifier.fit(X, y, epochs=epochs, batch_size=batch_size, verbose=2)\n",
    "            return self\n",
    "\n",
    "        def transform(self, X):\n",
    "            self.pred = self.classifier.predict_proba(X)\n",
    "            self.classes = [[0 if el < 0.2 else 1 for el in item] for item in self.pred]\n",
    "            return self.pred\n",
    "\n",
    "\n",
    "    def identity(self, arg):\n",
    "        \"\"\"\n",
    "        Simple identity function works as a passthrough.\n",
    "        \"\"\"\n",
    "        return arg\n",
    "\n",
    "\n",
    "    def reshape_a_feature_column(self, series):\n",
    "        return np.reshape(np.asarray(series), (len(series), 1))\n",
    "\n",
    "\n",
    "    def pipelinize_feature(self, function, active=True):\n",
    "        def list_comprehend_a_function(list_or_series, active=True):\n",
    "            if active:\n",
    "                processed = [function(i) for i in list_or_series]\n",
    "                processed = self.reshape_a_feature_column(processed)\n",
    "                return processed\n",
    "            else:\n",
    "                return self.reshape_a_feature_column(np.zeros(len(list_or_series)))\n",
    "\n",
    "\n",
    "    def get_text_length(self, text):\n",
    "        return len(text)\n",
    "\n",
    "\n",
    "    def multiclass_accuracy(self,predictions, target):\n",
    "        \"Returns the multiclass accuracy of the classifier's predictions\"\n",
    "        score = []\n",
    "        for j in range(0, 5):\n",
    "            count = 0\n",
    "            for i in range(len(predictions)):\n",
    "                if predictions[i][j] == target[i][j]:\n",
    "                    count += 1\n",
    "            score.append(count / len(predictions))\n",
    "        return score\n",
    "\n",
    "\n",
    "    def run(self, X, model_name):\n",
    "        \"\"\"\n",
    "        Returns the predictions from the pipeline including our NLTKPreprocessor and SVM classifier.\n",
    "        \"\"\"\n",
    "        save_path = \"./Models/\"\n",
    "        with open(save_path + model_name, 'rb') as f:\n",
    "            model = dill.load(f)\n",
    "        y_pred = model.predict_proba([X])\n",
    "        return y_pred"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [],
   "source": [
    "pred = predict_svm().run(X_test[4], model_name = \"Personality_traits_SVM\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.6"
  },
  "widgets": {
   "state": {},
   "version": "1.1.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
